From 78e98da1dbca01dcca2ce9dda7d0e48bfcca0df7 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Tue, 18 Feb 2020 18:48:34 -0800 Subject: [PATCH] test/e2e/framework/log: optimize PrunedStack() Use bytes instead of strings, and slice in-place filter (see https://github.com/golang/go/wiki/SliceTricks#filter-in-place) to avoid copying strings around. In my benchmark it shows almost 2x improvement: BenchmarkString-8 1477207 10198 ns/op BenchmarkBuffer-8 1561291 7622 ns/op BenchmarkInPlace-8 2295714 5202 ns/op String is the original implementation, Buffer is an intermediary one that uses strings.Builder, and InPlace is the one from this commit. Signed-off-by: Kir Kolyshkin --- test/e2e/framework/log.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/test/e2e/framework/log.go b/test/e2e/framework/log.go index 8f0e66b1cfb..fbc5dd02e18 100644 --- a/test/e2e/framework/log.go +++ b/test/e2e/framework/log.go @@ -17,10 +17,10 @@ limitations under the License. package framework import ( + "bytes" "fmt" "regexp" "runtime/debug" - "strings" "time" "github.com/onsi/ginkgo" @@ -78,12 +78,14 @@ var codeFilterRE = regexp.MustCompile(`/github.com/onsi/ginkgo/`) // This is a modified copy of PruneStack in https://github.com/onsi/ginkgo/blob/f90f37d87fa6b1dd9625e2b1e83c23ffae3de228/internal/codelocation/code_location.go#L25: // - simplified API and thus renamed (calls debug.Stack() instead of taking a parameter) // - source code filtering updated to be specific to Kubernetes -func PrunedStack(skip int) string { - fullStackTrace := string(debug.Stack()) - stack := strings.Split(fullStackTrace, "\n") +// - optimized to use bytes and in-place slice filtering from +// https://github.com/golang/go/wiki/SliceTricks#filter-in-place +func PrunedStack(skip int) []byte { + fullStackTrace := debug.Stack() + stack := bytes.Split(fullStackTrace, []byte("\n")) // Ensure that the even entries are the method names and the // the odd entries the source code information. - if len(stack) > 0 && strings.HasPrefix(stack[0], "goroutine ") { + if len(stack) > 0 && bytes.HasPrefix(stack[0], []byte("goroutine ")) { // Ignore "goroutine 29 [running]:" line. stack = stack[1:] } @@ -94,13 +96,16 @@ func PrunedStack(skip int) string { if len(stack) > 2*skip { stack = stack[2*skip:] } - prunedStack := []string{} + n := 0 for i := 0; i < len(stack)/2; i++ { // We filter out based on the source code file name. if !codeFilterRE.Match([]byte(stack[i*2+1])) { - prunedStack = append(prunedStack, stack[i*2]) - prunedStack = append(prunedStack, stack[i*2+1]) + stack[n] = stack[i*2] + stack[n+1] = stack[i*2+1] + n += 2 } } - return strings.Join(prunedStack, "\n") + stack = stack[:n] + + return bytes.Join(stack, []byte("\n")) }