You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2026-06-19 21:45:50 +02:00
attribute: inline hashing for small SLICE and MAP values (#8476)
Fixes #8450 Inlining the hashing logic for SLICE and MAP values of sizes 1 to 5 avoids slice header construction and function call overhead. Our benchmark results show: - Hashing speed improved by ~20% to ~44% across small slices and maps - Heap allocations are completely eliminated (reduced from 1 to 0 allocs/op) ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/attribute cpu: 13th Gen Intel(R) Core(TM) i7-13800H │ main.txt │ new.txt │ │ sec/op │ sec/op vs base │ HashKVs/All-20 1.649µ ± 2% 1.558µ ± 2% -5.49% (p=0.000 n=10) HashKVs/BoolTrue-20 155.15n ± 15% 32.38n ± 0% -79.13% (p=0.000 n=10) HashKVs/BoolFalse-20 167.95n ± 8% 32.35n ± 0% -80.74% (p=0.000 n=10) HashKVs/BoolSliceLen2-20 186.95n ± 22% 42.20n ± 0% -77.43% (p=0.000 n=10) HashKVs/BoolSliceLen3-20 201.80n ± 10% 53.74n ± 1% -73.37% (p=0.000 n=10) HashKVs/IntNegative-20 173.40n ± 16% 32.34n ± 1% -81.35% (p=0.000 n=10) HashKVs/IntZero-20 158.25n ± 9% 32.36n ± 0% -79.55% (p=0.000 n=10) HashKVs/IntSliceLen5-20 197.00n ± 4% 67.32n ± 1% -65.83% (p=0.000 n=10) HashKVs/IntSliceLen1-20 155.85n ± 10% 36.02n ± 0% -76.89% (p=0.000 n=10) HashKVs/Int64One-20 119.30n ± 12% 32.37n ± 0% -72.86% (p=0.000 n=10) HashKVs/Int64-20 145.25n ± 13% 32.37n ± 0% -77.71% (p=0.000 n=10) HashKVs/Int64SliceLen4-20 159.15n ± 8% 61.33n ± 0% -61.46% (p=0.000 n=10) HashKVs/Int64SliceWithZero-20 159.05n ± 8% 61.39n ± 1% -61.40% (p=0.000 n=10) HashKVs/Float64-20 113.15n ± 7% 32.34n ± 0% -71.41% (p=0.000 n=10) HashKVs/Float64Large-20 112.80n ± 6% 32.39n ± 0% -71.29% (p=0.000 n=10) HashKVs/Float64SliceLen4-20 162.45n ± 7% 60.64n ± 1% -62.67% (p=0.000 n=10) HashKVs/Float64SliceLarge-20 151.85n ± 3% 60.73n ± 2% -60.00% (p=0.000 n=10) HashKVs/StringFoo-20 109.35n ± 8% 30.26n ± 0% -72.32% (p=0.000 n=10) HashKVs/StringBar-20 105.60n ± 8% 30.27n ± 0% -71.33% (p=0.000 n=10) HashKVs/StringSliceLen3-20 139.35n ± 5% 48.50n ± 0% -65.20% (p=0.000 n=10) HashKVs/StringSliceLooksLikeIntSlice-20 118.50n ± 6% 34.91n ± 0% -70.54% (p=0.000 n=10) HashKVs/ByteSliceFoo-20 108.00n ± 6% 30.25n ± 0% -72.00% (p=0.000 n=10) HashKVs/ByteSliceLooksLikeIntSlice-20 105.80n ± 9% 31.39n ± 1% -70.33% (p=0.000 n=10) HashKVs/SliceLen0-20 110.15n ± 50% 29.69n ± 0% -73.05% (p=0.000 n=10) HashKVs/SliceLen1-20 127.70n ± 9% 44.95n ± 0% -64.80% (p=0.000 n=10) HashKVs/SliceLen2-20 160.35n ± 7% 67.96n ± 1% -57.61% (p=0.000 n=10) HashKVs/SliceLen3-20 190.90n ± 15% 86.01n ± 0% -54.95% (p=0.000 n=10) HashKVs/SliceLen4-20 216.5n ± 7% 105.4n ± 0% -51.32% (p=0.000 n=10) HashKVs/SliceLen5-20 243.0n ± 11% 122.3n ± 0% -49.67% (p=0.000 n=10) HashKVs/SliceNested-20 313.5n ± 9% 206.5n ± 0% -34.11% (p=0.000 n=10) HashKVs/MapLen0-20 105.55n ± 7% 29.68n ± 0% -71.88% (p=0.000 n=10) HashKVs/MapLen2-20 172.90n ± 7% 73.95n ± 1% -57.23% (p=0.000 n=10) HashKVs/MapNested-20 277.4n ± 4% 170.9n ± 0% -38.39% (p=0.000 n=10) HashKVs/EmptyValue-20 107.95n ± 6% 29.68n ± 0% -72.51% (p=0.000 n=10) HashValueSlice/Len2-20 140.20n ± 5% 49.48n ± 0% -64.70% (p=0.000 n=10) HashValueSlice/Len5-20 206.2n ± 9% 110.0n ± 1% -46.64% (p=0.000 n=10) HashValueSlice/Len8Nested-20 332.3n ± 4% 270.9n ± 0% -18.46% (p=0.000 n=10) HashValueMap/Len2-20 152.95n ± 32% 67.89n ± 1% -55.61% (p=0.000 n=10) HashValueMap/Len5-20 247.8n ± 3% 135.5n ± 0% -45.35% (p=0.000 n=10) HashValueMap/Len8Nested-20 412.7n ± 3% 342.1n ± 1% -17.11% (p=0.000 n=10) geomean 171.6n 60.79n -64.57% │ main.txt │ new.txt │ │ B/op │ B/op vs base │ HashKVs/All-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/BoolTrue-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/BoolFalse-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/BoolSliceLen2-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/BoolSliceLen3-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/IntNegative-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/IntZero-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/IntSliceLen5-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/IntSliceLen1-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/Int64One-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/Int64-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/Int64SliceLen4-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/Int64SliceWithZero-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/Float64-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/Float64Large-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/Float64SliceLen4-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/Float64SliceLarge-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/StringFoo-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/StringBar-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/StringSliceLen3-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/StringSliceLooksLikeIntSlice-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/ByteSliceFoo-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/ByteSliceLooksLikeIntSlice-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen0-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen1-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen2-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen3-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen4-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen5-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceNested-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/MapLen0-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/MapLen2-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/MapNested-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashKVs/EmptyValue-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashValueSlice/Len2-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashValueSlice/Len5-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashValueSlice/Len8Nested-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashValueMap/Len2-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashValueMap/Len5-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) HashValueMap/Len8Nested-20 80.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) geomean 80.00 ? ¹ ² ¹ summaries must be >0 to compute geomean ² ratios must be >0 to compute geomean │ main.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ HashKVs/All-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/BoolTrue-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/BoolFalse-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/BoolSliceLen2-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/BoolSliceLen3-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/IntNegative-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/IntZero-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/IntSliceLen5-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/IntSliceLen1-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/Int64One-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/Int64-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/Int64SliceLen4-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/Int64SliceWithZero-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/Float64-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/Float64Large-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/Float64SliceLen4-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/Float64SliceLarge-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/StringFoo-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/StringBar-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/StringSliceLen3-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/StringSliceLooksLikeIntSlice-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/ByteSliceFoo-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/ByteSliceLooksLikeIntSlice-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen0-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen1-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen2-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen3-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen4-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceLen5-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/SliceNested-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/MapLen0-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/MapLen2-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/MapNested-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashKVs/EmptyValue-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashValueSlice/Len2-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashValueSlice/Len5-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashValueSlice/Len8Nested-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashValueMap/Len2-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashValueMap/Len5-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) HashValueMap/Len8Nested-20 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) geomean 1.000 ? ¹ ² ¹ summaries must be >0 to compute geomean ² ratios must be >0 to compute geomean ```
This commit is contained in:
+45
-25
@@ -102,15 +102,25 @@ func hashValue(h xxhash.Hash, v Value) xxhash.Hash {
|
||||
case [0]Value:
|
||||
// No values to hash, but the type identifier is still hashed above.
|
||||
case [1]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
h = hashValue(h, vals[0])
|
||||
case [2]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
h = hashValue(h, vals[0])
|
||||
h = hashValue(h, vals[1])
|
||||
case [3]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
h = hashValue(h, vals[0])
|
||||
h = hashValue(h, vals[1])
|
||||
h = hashValue(h, vals[2])
|
||||
case [4]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
h = hashValue(h, vals[0])
|
||||
h = hashValue(h, vals[1])
|
||||
h = hashValue(h, vals[2])
|
||||
h = hashValue(h, vals[3])
|
||||
case [5]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
h = hashValue(h, vals[0])
|
||||
h = hashValue(h, vals[1])
|
||||
h = hashValue(h, vals[2])
|
||||
h = hashValue(h, vals[3])
|
||||
h = hashValue(h, vals[4])
|
||||
default:
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
@@ -123,15 +133,40 @@ func hashValue(h xxhash.Hash, v Value) xxhash.Hash {
|
||||
case [0]KeyValue:
|
||||
// No values to hash, but the type identifier is still hashed above.
|
||||
case [1]KeyValue:
|
||||
h = hashMap(h, vals[:])
|
||||
h = h.String(string(vals[0].Key))
|
||||
h = hashValue(h, vals[0].Value)
|
||||
case [2]KeyValue:
|
||||
h = hashMap(h, vals[:])
|
||||
h = h.String(string(vals[0].Key))
|
||||
h = hashValue(h, vals[0].Value)
|
||||
h = h.String(string(vals[1].Key))
|
||||
h = hashValue(h, vals[1].Value)
|
||||
case [3]KeyValue:
|
||||
h = hashMap(h, vals[:])
|
||||
h = h.String(string(vals[0].Key))
|
||||
h = hashValue(h, vals[0].Value)
|
||||
h = h.String(string(vals[1].Key))
|
||||
h = hashValue(h, vals[1].Value)
|
||||
h = h.String(string(vals[2].Key))
|
||||
h = hashValue(h, vals[2].Value)
|
||||
case [4]KeyValue:
|
||||
h = hashMap(h, vals[:])
|
||||
h = h.String(string(vals[0].Key))
|
||||
h = hashValue(h, vals[0].Value)
|
||||
h = h.String(string(vals[1].Key))
|
||||
h = hashValue(h, vals[1].Value)
|
||||
h = h.String(string(vals[2].Key))
|
||||
h = hashValue(h, vals[2].Value)
|
||||
h = h.String(string(vals[3].Key))
|
||||
h = hashValue(h, vals[3].Value)
|
||||
case [5]KeyValue:
|
||||
h = hashMap(h, vals[:])
|
||||
h = h.String(string(vals[0].Key))
|
||||
h = hashValue(h, vals[0].Value)
|
||||
h = h.String(string(vals[1].Key))
|
||||
h = hashValue(h, vals[1].Value)
|
||||
h = h.String(string(vals[2].Key))
|
||||
h = hashValue(h, vals[2].Value)
|
||||
h = h.String(string(vals[3].Key))
|
||||
h = hashValue(h, vals[3].Value)
|
||||
h = h.String(string(vals[4].Key))
|
||||
h = hashValue(h, vals[4].Value)
|
||||
default:
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
@@ -151,18 +186,3 @@ func hashValue(h xxhash.Hash, v Value) xxhash.Hash {
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func hashValueSlice(h xxhash.Hash, vals []Value) xxhash.Hash {
|
||||
for _, v := range vals {
|
||||
h = hashValue(h, v)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func hashMap(h xxhash.Hash, vals []KeyValue) xxhash.Hash {
|
||||
for _, kv := range vals {
|
||||
h = h.String(string(kv.Key))
|
||||
h = hashValue(h, kv.Value)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user