Skip to content

Commit 55d0a17

Browse files
Improve dictionary struct encoder in Go (#156)
Related to #153 ### Summary of changes - Structs can be now marked frozen, i.e. unchangeable. Once frozen they can be safely referenced by pointer from multiple places (including dictionaries) without cloning. - We now have Set methods on fields that are dictionary structs. The setter works fast if the value being set is frozen - instead of full copy it just copies the pointer. - CopyFrom does a similar optimization. Any encounter of a frozen struct just copies the pointer. - Structs which are encoded remember (cache) their refNum. Next attempt to encode the same struct no longer requires slow dictionary lookup by value. - OTLP to STEF converters use Freeze() when creating data, which improves encoding performance. ### Performance impact Performance improvement of encoding of dictionary structs drammatic - about *10x faster* and *5 times less memory*. Here is Profile serialization benchmarks: ``` pkg: github.com/splunk/stef/examples/profile cpu: Apple M2 Pro │ bench_base.txt │ bench_current.txt │ │ sec/op │ sec/op vs base │ Serialization/file=deser_stef.prof/format=stef-10 2901.2µ ± 4% 471.7µ ± 1% -83.74% (p=0.002 n=6) Serialization/file=otelcol_otlp.prof/format=stef-10 660.41µ ± 2% 62.30µ ± 1% -90.57% (p=0.002 n=6) Serialization/file=wsstreamasync.prof/format=stef-10 7253.3µ ± 3% 672.3µ ± 1% -90.73% (p=0.002 n=6) geomean 2.404m 270.3µ -88.76% │ bench_base.txt │ bench_current.txt │ │ sec/sample │ sec/sample vs base │ Serialization/file=deser_stef.prof/format=stef-10 2637.5n ± 4% 428.9n ± 1% -83.74% (p=0.002 n=6) Serialization/file=otelcol_otlp.prof/format=stef-10 7591.0n ± 2% 716.0n ± 1% -90.57% (p=0.002 n=6) Serialization/file=wsstreamasync.prof/format=stef-10 6523.0n ± 3% 604.6n ± 1% -90.73% (p=0.002 n=6) geomean 5.074µ 570.5n -88.76% │ bench_base.txt │ bench_current.txt │ │ B/op │ B/op vs base │ Serialization/file=deser_stef.prof/format=stef-10 1961.0Ki ± 0% 448.9Ki ± 0% -77.11% (p=0.002 n=6) Serialization/file=otelcol_otlp.prof/format=stef-10 624.63Ki ± 0% 73.44Ki ± 0% -88.24% (p=0.002 n=6) Serialization/file=wsstreamasync.prof/format=stef-10 3288.5Ki ± 0% 716.1Ki ± 0% -78.22% (p=0.002 n=6) geomean 1.554Mi 286.9Ki -81.97% │ bench_base.txt │ bench_current.txt │ │ allocs/op │ allocs/op vs base │ Serialization/file=deser_stef.prof/format=stef-10 12007.0 ± 0% 989.0 ± 0% -91.76% (p=0.002 n=6) Serialization/file=otelcol_otlp.prof/format=stef-10 2353.0 ± 0% 330.0 ± 0% -85.98% (p=0.002 n=6) Serialization/file=wsstreamasync.prof/format=stef-10 27628.0 ± 0% 931.0 ± 0% -96.63% (p=0.002 n=6) geomean 9.207k 672.3 -92.70% ``` It comes at a cost of a percent slower deserialization due to additional bookkeeping, which is an acceptable tradeoff since deserialization is already very fast: ``` pkg: github.com/splunk/stef/examples/profile cpu: Apple M2 Pro │ bench_base.txt │ bench_current.txt │ │ sec/op │ sec/op vs base │ Deserialization/file=deser_stef.prof/format=stef-10 486.8µ ± 1% 519.4µ ± 1% +6.70% (p=0.000 n=9) Deserialization/file=otelcol_otlp.prof/format=stef-10 113.5µ ± 2% 115.2µ ± 1% ~ (p=0.063 n=9) Deserialization/file=wsstreamasync.prof/format=stef-10 933.2µ ± 3% 958.4µ ± 2% +2.70% (p=0.000 n=9) geomean 372.2µ 385.6µ +3.60% │ bench_base.txt │ bench_current.txt │ │ sec/sample │ sec/sample vs base │ Deserialization/file=deser_stef.prof/format=stef-10 442.6n ± 1% 472.2n ± 1% +6.69% (p=0.000 n=9) Deserialization/file=otelcol_otlp.prof/format=stef-10 1.305µ ± 2% 1.324µ ± 1% ~ (p=0.054 n=9) Deserialization/file=wsstreamasync.prof/format=stef-10 839.2n ± 3% 861.8n ± 2% +2.69% (p=0.000 n=9) geomean 785.5n 813.7n +3.59% │ bench_base.txt │ bench_current.txt │ │ B/op │ B/op vs base │ Deserialization/file=deser_stef.prof/format=stef-10 1.163Mi ± 0% 1.041Mi ± 0% -10.51% (p=0.000 n=9) Deserialization/file=otelcol_otlp.prof/format=stef-10 390.6Ki ± 0% 360.6Ki ± 0% -7.67% (p=0.000 n=9) Deserialization/file=wsstreamasync.prof/format=stef-10 1.590Mi ± 0% 1.507Mi ± 0% -5.23% (p=0.000 n=9) geomean 911.6Ki 840.3Ki -7.83% │ bench_base.txt │ bench_current.txt │ │ allocs/op │ allocs/op vs base │ Deserialization/file=deser_stef.prof/format=stef-10 1.673k ± 0% 1.638k ± 0% -2.09% (p=0.000 n=9) Deserialization/file=otelcol_otlp.prof/format=stef-10 627.0 ± 0% 618.0 ± 0% -1.44% (p=0.000 n=9) Deserialization/file=wsstreamasync.prof/format=stef-10 1.523k ± 0% 1.492k ± 0% -2.04% (p=0.000 n=9) geomean 1.169k 1.147k -1.85% ``` The slowdown is less pronounced for Otel schema: ``` pkg: github.com/splunk/stef/benchmarks cpu: Apple M2 Pro │ bench_base.txt │ bench_current.txt │ │ sec/op │ sec/op vs base │ DeserializeNative/STEF/deser-10 1.552m ± 0% 1.612m ± 0% +3.89% (p=0.000 n=30) │ bench_base.txt │ bench_current.txt │ │ sec/point │ sec/point vs base │ DeserializeNative/STEF/deser-10 23.21n ± 0% 24.11n ± 0% +3.90% (p=0.000 n=30) │ bench_base.txt │ bench_current.txt │ │ B/op │ B/op vs base │ DeserializeNative/STEF/deser-10 911.5Ki ± 0% 925.4Ki ± 0% +1.53% (p=0.000 n=30) │ bench_base.txt │ bench_current.txt │ │ allocs/op │ allocs/op vs base │ DeserializeNative/STEF/deser-10 669.0 ± 0% 465.0 ± 0% -30.49% (p=0.000 n=30) ``` Converting from pdata to STEF also benefits significantly from the faster encoding: ``` pkg: github.com/splunk/stef/benchmarks cpu: Apple M2 Pro │ bench_base.txt │ bench_current.txt │ │ sec/op │ sec/op vs base │ SerializeFromPdata/STEF/serialize-10 92.83m ± 0% 64.01m ± 0% -31.05% (p=0.000 n=15) │ bench_base.txt │ bench_current.txt │ │ sec/point │ sec/point vs base │ SerializeFromPdata/STEF/serialize-10 1388.0n ± 0% 957.5n ± 0% -31.02% (p=0.000 n=15) │ bench_base.txt │ bench_current.txt │ │ B/op │ B/op vs base │ SerializeFromPdata/STEF/serialize-10 169.20Mi ± 0% 78.28Mi ± 0% -53.74% (p=0.000 n=15) │ bench_base.txt │ bench_current.txt │ │ allocs/op │ allocs/op vs base │ SerializeFromPdata/STEF/serialize-10 256.4k ± 0% 134.8k ± 0% -47.43% (p=0.000 n=15) ```
1 parent fb37d45 commit 55d0a17

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+5074
-3587
lines changed

benchmarks/benchmarks_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ func addZstdCompressTime(
6161
bodyBytes []byte,
6262
dataPointCount int,
6363
) {
64+
if !chartsEnabled() {
65+
return
66+
}
67+
6468
b.Run(
6569
fmt.Sprintf("%s/zstd", encoding.Name()),
6670
func(b *testing.B) {
@@ -87,6 +91,10 @@ func addZstdDecompressTime(
8791
bodyBytes []byte,
8892
dataPointCount int,
8993
) {
94+
if !chartsEnabled() {
95+
return
96+
}
97+
9098
zstdBytes := testutils.CompressZstd(bodyBytes)
9199
if zstdBytes == nil {
92100
log.Fatal("compression failed")

benchmarks/charts.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ type BarOutput struct {
2121
enabled bool
2222
}
2323

24+
func chartsEnabled() bool {
25+
return os.Getenv("UPDATE_BENCH_HTML") != ""
26+
}
27+
2428
func (c *BarOutput) BeginChart(title string, t testing.TB) {
2529
c.t = t
2630
c.title = title
@@ -124,7 +128,7 @@ func (c *BarOutput) RecordStacked(b *testing.B, encoding string, series string,
124128
}
125129

126130
func (c *BarOutput) Begin() {
127-
if os.Getenv("UPDATE_BENCH_HTML") == "" {
131+
if !chartsEnabled() {
128132
return
129133
}
130134
c.enabled = true

benchmarks/readwrite_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,15 +207,15 @@ func copyModified(dst *oteltef.Metrics, src *oteltef.Metrics) {
207207
}
208208

209209
if src.IsResourceModified() {
210-
dst.Resource().CopyFrom(src.Resource())
210+
dst.SetResource(src.Resource())
211211
}
212212

213213
if src.IsScopeModified() {
214-
dst.Scope().CopyFrom(src.Scope())
214+
dst.SetScope(src.Scope())
215215
}
216216

217217
if src.IsMetricModified() {
218-
dst.Metric().CopyFrom(src.Metric())
218+
dst.SetMetric(src.Metric())
219219
}
220220

221221
if src.IsAttributesModified() {

examples/jsonl/internal/jsonstef/jsonobject.go

Lines changed: 38 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/jsonl/internal/jsonstef/jsonvalue.go

Lines changed: 87 additions & 59 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)