forked from cockroachdb/cockroach
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
87968: tree: Improve performance of tree.AsJSON r=miretskiy a=miretskiy Improve performance of `tree.AsJSON` method. These improvements are important for any query that produces large number of JSON objects, as well as to changefeeds, which rely on this function when producing JSON encoded feed. Most of the changes revolved around modifying underlying types (s.a. date/timestamp types, box2d, etc) to favor using functions that append to bytes buffer, instead of relying on slower functions, such as `fmt.Sprintf`. The conversion performance improved around 5-10% for most of the types, and as high as 50% for time types: ``` Benchmark old t/op new t/op delta AsJSON/box2d-10 578ns ± 3% 414ns ± 2% -28.49% (p=0.000 n=10+9) AsJSON/box2d[]-10 1.64µs ± 3% 1.19µs ± 4% -27.14% (p=0.000 n=10+10) AsJSON/time-10 232ns ± 2% 103ns ± 1% -55.61% (p=0.000 n=10+10) AsJSON/time[]-10 687ns ± 4% 342ns ± 4% -50.17% (p=0.000 n=10+10) ``` Note: Some types in the local benchmark show slight slow down in speed. No changes were made in those types, and in general, the encoding speed of these types might be too fast to reliable detect changes: ``` Benchmark old t/op new t/op delta AsJSON/bool[]-10 65.9ns ± 1% 67.7ns ± 2% +2.79% (p=0.001 n=8+9) ``` The emphasis was also placed on reducing allocations. By relying more heavily on a pooled FmtCtx, which contains bytes buffer, some conversions resulted in amortized elimination of allocations (time): ``` Benchmark old B/op new t/op delta AsJSON/timestamp-10 42.1B ± 3% 0.0B -100.00% (p=0.000 n=10+10) AsJSON/timestamp[]-10 174B ± 4% 60B ± 1% -65.75% (p=0.000 n=10+10) ``` Release Note: None Release Justification: performance improvement Co-authored-by: Yevgeniy Miretskiy <[email protected]>
- Loading branch information
Showing
16 changed files
with
313 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Copyright 2022 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package tree_test | ||
|
||
import ( | ||
"math/rand" | ||
"testing" | ||
"time" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/sql/randgen" | ||
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree" | ||
"github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb" | ||
"github.com/cockroachdb/cockroach/pkg/sql/types" | ||
"github.com/cockroachdb/cockroach/pkg/util/randutil" | ||
) | ||
|
||
func BenchmarkAsJSON(b *testing.B) { | ||
// Use fixed seed so that each invocation of this benchmark | ||
// produces exactly the same types, and datums streams. | ||
// This number can be changed to an arbitrary value; doing so | ||
// would result in new types/datums being produced. | ||
rng := randutil.NewTestRandWithSeed(-4365865412074131521) | ||
|
||
const numDatums = 1024 | ||
makeDatums := func(typ *types.T) tree.Datums { | ||
const allowNulls = true | ||
res := make(tree.Datums, numDatums) | ||
for i := 0; i < numDatums; i++ { | ||
res[i] = randgen.RandDatum(rng, typ, allowNulls) | ||
} | ||
return res | ||
} | ||
|
||
bench := func(b *testing.B, typ *types.T) { | ||
b.ReportAllocs() | ||
b.StopTimer() | ||
datums := makeDatums(typ) | ||
b.StartTimer() | ||
|
||
for i := 0; i < b.N; i++ { | ||
_, err := tree.AsJSON(datums[i%numDatums], sessiondatapb.DataConversionConfig{}, time.UTC) | ||
if err != nil { | ||
b.Fatal(err) | ||
} | ||
} | ||
} | ||
|
||
for _, typ := range testTypes(rng) { | ||
b.Run(typ.String(), func(b *testing.B) { | ||
bench(b, typ) | ||
}) | ||
|
||
if randgen.IsAllowedForArray(typ) { | ||
typ = types.MakeArray(typ) | ||
b.Run(typ.String(), func(b *testing.B) { | ||
bench(b, typ) | ||
}) | ||
} | ||
} | ||
} | ||
|
||
// testTypes returns list of types to test against. | ||
func testTypes(rng *rand.Rand) (typs []*types.T) { | ||
for _, typ := range randgen.SeedTypes { | ||
switch typ { | ||
case types.AnyTuple: | ||
// Ignore AnyTuple -- it's not very interesting; we'll generate test tuples below. | ||
case types.RegClass, types.RegNamespace, types.RegProc, types.RegProcedure, types.RegRole, types.RegType: | ||
// Ignore a bunch of pseudo-OID types (just want regular OID). | ||
case types.Geometry, types.Geography: | ||
// Ignore geometry/geography: these types are insanely inefficient; | ||
// AsJson(Geo) -> MarshalGeo -> go JSON bytes -> ParseJSON -> Go native -> json.JSON | ||
// Benchmarking this generates too much noise. | ||
// TODO(yevgeniy): fix this. | ||
default: | ||
typs = append(typs, typ) | ||
} | ||
} | ||
|
||
// Add tuple types. | ||
var tupleTypes []*types.T | ||
makeTupleType := func() *types.T { | ||
contents := make([]*types.T, rng.Intn(6)) // Up to 6 fields | ||
for i := range contents { | ||
contents[i] = randgen.RandTypeFromSlice(rng, typs) | ||
} | ||
candidateTuple := types.MakeTuple(contents) | ||
// Ensure tuple type is unique. | ||
for _, t := range tupleTypes { | ||
if t.Equal(candidateTuple) { | ||
return nil | ||
} | ||
} | ||
tupleTypes = append(tupleTypes, candidateTuple) | ||
return candidateTuple | ||
} | ||
|
||
const numTupleTypes = 5 | ||
for i := 0; i < numTupleTypes; i++ { | ||
var typ *types.T | ||
for typ == nil { | ||
typ = makeTupleType() | ||
} | ||
typs = append(typs, typ) | ||
} | ||
|
||
return typs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
load("//build/bazelutil/unused_checker:unused.bzl", "get_x_data") | ||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") | ||
|
||
go_library( | ||
name = "strutil", | ||
srcs = ["util.go"], | ||
importpath = "github.com/cockroachdb/cockroach/pkg/util/strutil", | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
go_test( | ||
name = "strutil_test", | ||
srcs = ["util_test.go"], | ||
args = ["-test.timeout=295s"], | ||
embed = [":strutil"], | ||
deps = ["@com_github_stretchr_testify//require"], | ||
) | ||
|
||
get_x_data(name = "get_x_data") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Copyright 2022 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package strutil | ||
|
||
import "strconv" | ||
|
||
// AppendInt appends the decimal form of x to b and returns the result. | ||
// If the decimal form is shorter than width, the result is padded with leading 0's. | ||
// If the decimal is longer than width, returns formatted decimal without | ||
// any truncation. | ||
func AppendInt(b []byte, x int, width int) []byte { | ||
if x < 0 { | ||
width-- | ||
x = -x | ||
b = append(b, '-') | ||
} | ||
|
||
var scratch [16]byte | ||
xb := strconv.AppendInt(scratch[:0], int64(x), 10) | ||
|
||
// Add 0-padding. | ||
for w := len(xb); w < width; w++ { | ||
b = append(b, '0') | ||
} | ||
return append(b, xb...) | ||
} |
Oops, something went wrong.