From 356bc4d6c6e4abab163842dd1891da60087a5280 Mon Sep 17 00:00:00 2001 From: Frederic Branczyk Date: Fri, 4 Aug 2023 17:37:57 +0200 Subject: [PATCH] pkg/query: Add fast map of strings to json marshaling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This has a ~82% improvement compared to the previously used method. ``` $ benchstat stdlib.txt ours.txt name old time/op new time/op delta MarshalMap 412ns ± 1% 73ns ± 0% -82.34% (p=0.008 n=5+5) ``` Which results in an overall improvement of ~44% for real life profiling data that has labels: ``` $ benchstat old.txt new.txt name old time/op new time/op delta ArrowFlamegraph-10 289ms ± 1% 161ms ± 1% -44.23% (p=0.008 n=5+5) ``` --- pkg/query/flamegraph_arrow.go | 8 +- pkg/query/flamegraph_arrow_test.go | 76 ++++++++ pkg/query/json.go | 173 +++++++++++++++++++ pkg/query/query_test.go | 2 +- pkg/query/testdata/profile-with-labels.pb.gz | Bin 0 -> 28557 bytes 5 files changed, 253 insertions(+), 6 deletions(-) create mode 100644 pkg/query/json.go create mode 100644 pkg/query/testdata/profile-with-labels.pb.gz diff --git a/pkg/query/flamegraph_arrow.go b/pkg/query/flamegraph_arrow.go index 3bbe3e20920..02e0fbe8f05 100644 --- a/pkg/query/flamegraph_arrow.go +++ b/pkg/query/flamegraph_arrow.go @@ -16,7 +16,6 @@ package query import ( "bytes" "context" - "encoding/json" "fmt" "strings" @@ -400,6 +399,7 @@ func generateFlamegraphArrowRecord(ctx context.Context, mem memory.Allocator, tr } } } + lsbytes := make([]byte, 0, 512) for i := 0; i < fb.builderCumulative.Len(); i++ { if lsets, hasLabels := fb.labels[i]; hasLabels { inter := mapsIntersection(lsets) @@ -408,10 +408,8 @@ func generateFlamegraphArrowRecord(ctx context.Context, mem memory.Allocator, tr continue } - lsbytes, err := json.Marshal(inter) - if err != nil { - return nil, 0, 0, 0, err - } + lsbytes = lsbytes[:0] + lsbytes = MarshalStringMap(lsbytes, inter) if err := fb.builderLabels.Append(lsbytes); err != nil { return nil, 0, 0, 0, err } diff --git a/pkg/query/flamegraph_arrow_test.go b/pkg/query/flamegraph_arrow_test.go index db54d411c5a..94196c1c4f6 100644 --- a/pkg/query/flamegraph_arrow_test.go +++ b/pkg/query/flamegraph_arrow_test.go @@ -15,8 +15,11 @@ package query import ( "bytes" + "compress/gzip" "context" "encoding/json" + "io" + "os" "testing" "github.com/apache/arrow/go/v13/arrow" @@ -681,3 +684,76 @@ func TestMapsIntersection(t *testing.T) { {"thread": "2", "thread_name": "name"}, })) } + +func BenchmarkArrowFlamegraph(b *testing.B) { + fileContent, err := os.ReadFile("testdata/profile-with-labels.pb.gz") + require.NoError(b, err) + + gz, err := gzip.NewReader(bytes.NewBuffer(fileContent)) + require.NoError(b, err) + + decompressed, err := io.ReadAll(gz) + require.NoError(b, err) + + p := &pprofpb.Profile{} + require.NoError(b, p.UnmarshalVT(decompressed)) + + pp, err := pprofprofile.ParseData(fileContent) + require.NoError(b, err) + + np, err := PprofToSymbolizedProfile(parcaprofile.MetaFromPprof(p, "memory", 0), pp, 0) + require.NoError(b, err) + + tracer := trace.NewNoopTracerProvider().Tracer("") + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, err := GenerateFlamegraphArrow( + context.Background(), + memory.DefaultAllocator, + tracer, + np, + nil, + 0, + ) + require.NoError(b, err) + } +} + +func TestMarshalMap(t *testing.T) { + m := map[string]string{ + "test1": "something", + "test2": "something_else", + } + + buf := make([]byte, 0, 1024) + buf = MarshalStringMap(buf, m) + res := string(buf) + expected := []string{ + `{"test1":"something","test2":"something_else"}`, + `{"test2":"something_else","test1":"something"}`, + } + require.Contains(t, expected, res) +} + +func BenchmarkMarshalMap(b *testing.B) { + m := map[string]string{ + "test1": "something", + "test2": "something_else", + } + + var err error + b.ResetTimer() + b.Run("stdlib", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err = json.Marshal(m) + } + }) + _ = err + b.Run("ours", func(b *testing.B) { + buf := make([]byte, 0, 1024) + for i := 0; i < b.N; i++ { + buf = MarshalStringMap(buf, m) + } + }) +} diff --git a/pkg/query/json.go b/pkg/query/json.go new file mode 100644 index 00000000000..e1f2872a77d --- /dev/null +++ b/pkg/query/json.go @@ -0,0 +1,173 @@ +// Copyright 2023 The Parca Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package query + +import ( + "math/bits" + "reflect" + "unsafe" +) + +func MarshalStringMap(buf []byte, m map[string]string) []byte { + buf = buf[:0] + buf = append(buf, '{') + + for k, v := range m { + buf = appendString(buf, k) + buf = append(buf, ':') + buf = appendString(buf, v) + buf = append(buf, ',') + } + + buf[len(buf)-1] = '}' + return buf +} + +const ( + lsb = 0x0101010101010101 + msb = 0x8080808080808080 +) + +var hex = "0123456789abcdef" + +var needEscape = [256]bool{ + '"': true, + '\\': true, + 0x00: true, + 0x01: true, + 0x02: true, + 0x03: true, + 0x04: true, + 0x05: true, + 0x06: true, + 0x07: true, + 0x08: true, + 0x09: true, + 0x0a: true, + 0x0b: true, + 0x0c: true, + 0x0d: true, + 0x0e: true, + 0x0f: true, + 0x10: true, + 0x11: true, + 0x12: true, + 0x13: true, + 0x14: true, + 0x15: true, + 0x16: true, + 0x17: true, + 0x18: true, + 0x19: true, + 0x1a: true, + 0x1b: true, + 0x1c: true, + 0x1d: true, + 0x1e: true, + 0x1f: true, + /* 0x20 - 0xff */ +} + +// Taken from goccy/go-json. +func appendString(buf []byte, s string) []byte { + valLen := len(s) + if valLen == 0 { + return append(buf, `""`...) + } + buf = append(buf, '"') + var i, j int + if valLen >= 8 { + chunks := stringToUint64Slice(s) + for _, n := range chunks { + // combine masks before checking for the MSB of each byte. We include + // `n` in the mask to check whether any of the *input* byte MSBs were + // set (i.e. the byte was outside the ASCII range). + mask := n | (n - (lsb * 0x20)) | + ((n ^ (lsb * '"')) - lsb) | + ((n ^ (lsb * '\\')) - lsb) + if (mask & msb) != 0 { + j = bits.TrailingZeros64(mask&msb) / 8 + goto ESCAPE_END + } + } + valLen := len(s) + for i := len(chunks) * 8; i < valLen; i++ { + if needEscape[s[i]] { + j = i + goto ESCAPE_END + } + } + return append(append(buf, s...), '"') + } +ESCAPE_END: + for j < valLen { + c := s[j] + + if !needEscape[c] { + // fast path: most of the time, printable ascii characters are used + j++ + continue + } + + switch c { + case '\\', '"': + buf = append(buf, s[i:j]...) + buf = append(buf, '\\', c) + i = j + 1 + j = j + 1 + continue + + case '\n': + buf = append(buf, s[i:j]...) + buf = append(buf, '\\', 'n') + i = j + 1 + j = j + 1 + continue + + case '\r': + buf = append(buf, s[i:j]...) + buf = append(buf, '\\', 'r') + i = j + 1 + j = j + 1 + continue + + case '\t': + buf = append(buf, s[i:j]...) + buf = append(buf, '\\', 't') + i = j + 1 + j = j + 1 + continue + + case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F + buf = append(buf, s[i:j]...) + buf = append(buf, `\u00`...) + buf = append(buf, hex[c>>4], hex[c&0xF]) + i = j + 1 + j = j + 1 + continue + } + j++ + } + + return append(append(buf, s[i:]...), '"') +} + +func stringToUint64Slice(s string) []uint64 { + return *(*[]uint64)(unsafe.Pointer(&reflect.SliceHeader{ //nolint:govet + Data: ((*reflect.StringHeader)(unsafe.Pointer(&s))).Data, + Len: len(s) / 8, + Cap: len(s) / 8, + })) +} diff --git a/pkg/query/query_test.go b/pkg/query/query_test.go index 7ec41fceb69..cdee75cd996 100644 --- a/pkg/query/query_test.go +++ b/pkg/query/query_test.go @@ -136,7 +136,7 @@ func Benchmark_Query_Merge(b *testing.B) { }, }, //nolint:staticcheck // SA1019: Fow now we want to support these APIs - ReportType: pb.QueryRequest_REPORT_TYPE_FLAMEGRAPH_UNSPECIFIED, + ReportType: pb.QueryRequest_REPORT_TYPE_FLAMEGRAPH_ARROW, }) require.NoError(b, err) } diff --git a/pkg/query/testdata/profile-with-labels.pb.gz b/pkg/query/testdata/profile-with-labels.pb.gz new file mode 100644 index 0000000000000000000000000000000000000000..3143e3609afbda5ed59acf6c575f1be38a253c80 GIT binary patch literal 28557 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 literal 0 HcmV?d00001