-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[PoC] Use Set hash in Distinct #5028
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5028 +/- ##
=====================================
Coverage 83.5% 83.5%
=====================================
Files 238 240 +2
Lines 15757 15832 +75
=====================================
+ Hits 13159 13229 +70
- Misses 2309 2314 +5
Partials 289 289
|
I find this trade-off is acceptable because:
|
Where I would expect to see the real gains are in the metric aggregation benchmarks.
I do worry that most users will use |
benchstat old.out new.out
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/sdk/metric/internal/aggregate
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
│ old.out │ new.out │
│ sec/op │ sec/op vs base │
ExponentialHistogram/Int64/Cumulative/100/Measure-8 17.70µ ± 1% 11.12µ ± 1% -37.17% (p=0.000 n=10)
ExponentialHistogram/Int64/Cumulative/100/ComputeAggregation-8 6.353µ ± 2% 6.564µ ± 1% +3.31% (p=0.000 n=10)
ExponentialHistogram/Int64/Delta/100/Measure-8 19.09µ ± 3% 11.23µ ± 1% -41.18% (p=0.000 n=10)
ExponentialHistogram/Int64/Delta/100/ComputeAggregation-8 6.737µ ± 1% 6.891µ ± 18% +2.29% (p=0.000 n=10)
ExponentialHistogram/Float64/Cumulative/100/Measure-8 19.21µ ± 4% 11.38µ ± 2% -40.78% (p=0.000 n=10)
ExponentialHistogram/Float64/Cumulative/100/ComputeAggregation-8 6.365µ ± 1% 6.588µ ± 19% +3.51% (p=0.000 n=10)
ExponentialHistogram/Float64/Delta/100/Measure-8 19.71µ ± 3% 11.76µ ± 5% -40.35% (p=0.000 n=10)
ExponentialHistogram/Float64/Delta/100/ComputeAggregation-8 6.769µ ± 1% 6.911µ ± 1% +2.09% (p=0.000 n=10)
Histogram/Int64/Cumulative/100/Measure-8 16.492µ ± 5% 8.696µ ± 6% -47.27% (p=0.000 n=10)
Histogram/Int64/Cumulative/100/ComputeAggregation-8 8.978µ ± 0% 9.846µ ± 10% +9.67% (p=0.000 n=10)
Histogram/Int64/Delta/100/Measure-8 16.072µ ± 5% 8.351µ ± 2% -48.04% (p=0.000 n=10)
Histogram/Int64/Delta/100/ComputeAggregation-8 4.525µ ± 0% 4.550µ ± 2% +0.56% (p=0.009 n=10)
Histogram/Float64/Cumulative/100/Measure-8 16.654µ ± 3% 8.704µ ± 4% -47.74% (p=0.000 n=10)
Histogram/Float64/Cumulative/100/ComputeAggregation-8 9.050µ ± 1% 9.308µ ± 15% +2.85% (p=0.000 n=10)
Histogram/Float64/Delta/100/Measure-8 16.417µ ± 6% 8.605µ ± 3% -47.58% (p=0.000 n=10)
Histogram/Float64/Delta/100/ComputeAggregation-8 4.547µ ± 1% 4.459µ ± 1% -1.92% (p=0.000 n=10)
Sum/Int64/Cumulative/100/Measure-8 25.951µ ± 3% 9.133µ ± 5% -64.80% (p=0.000 n=10)
Sum/Int64/Cumulative/100/ComputeAggregation-8 2.458µ ± 1% 2.468µ ± 10% ~ (p=0.171 n=10)
Sum/Int64/Delta/100/Measure-8 26.016µ ± 3% 9.066µ ± 4% -65.15% (p=0.000 n=10)
Sum/Int64/Delta/100/ComputeAggregation-8 2.758µ ± 1% 2.783µ ± 0% +0.92% (p=0.001 n=10)
Sum/Float64/Cumulative/100/Measure-8 25.480µ ± 3% 9.607µ ± 5% -62.30% (p=0.000 n=10)
Sum/Float64/Cumulative/100/ComputeAggregation-8 2.459µ ± 0% 2.466µ ± 1% ~ (p=0.305 n=10)
Sum/Float64/Delta/100/Measure-8 25.425µ ± 4% 9.368µ ± 6% -63.16% (p=0.000 n=10)
Sum/Float64/Delta/100/ComputeAggregation-8 2.751µ ± 0% 2.794µ ± 5% +1.58% (p=0.000 n=10)
geomean 9.770µ 6.871µ -29.68%
│ old.out │ new.out │
│ B/op │ B/op vs base │
ExponentialHistogram/Int64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Int64/Cumulative/100/ComputeAggregation-8 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Int64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Int64/Delta/100/ComputeAggregation-8 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Float64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Float64/Cumulative/100/ComputeAggregation-8 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Float64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Float64/Delta/100/ComputeAggregation-8 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Int64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Int64/Cumulative/100/ComputeAggregation-8 2.391Ki ± 0% 2.391Ki ± 0% ~ (p=1.000 n=10) ¹
Histogram/Int64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Int64/Delta/100/ComputeAggregation-8 48.00 ± 0% 48.00 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Float64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Float64/Cumulative/100/ComputeAggregation-8 2.391Ki ± 0% 2.391Ki ± 0% ~ (p=1.000 n=10) ¹
Histogram/Float64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Float64/Delta/100/ComputeAggregation-8 48.00 ± 0% 48.00 ± 0% ~ (p=1.000 n=10) ¹
Sum/Int64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Int64/Cumulative/100/ComputeAggregation-8 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=10) ¹
Sum/Int64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Int64/Delta/100/ComputeAggregation-8 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=10) ¹
Sum/Float64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Float64/Cumulative/100/ComputeAggregation-8 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=10) ¹
Sum/Float64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Float64/Delta/100/ComputeAggregation-8 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=10) ¹
geomean ² +0.00% ²
¹ all samples are equal
² summaries must be >0 to compute geomean
│ old.out │ new.out │
│ allocs/op │ allocs/op vs base │
ExponentialHistogram/Int64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Int64/Cumulative/100/ComputeAggregation-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Int64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Int64/Delta/100/ComputeAggregation-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Float64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Float64/Cumulative/100/ComputeAggregation-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Float64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
ExponentialHistogram/Float64/Delta/100/ComputeAggregation-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Int64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Int64/Cumulative/100/ComputeAggregation-8 102.0 ± 0% 102.0 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Int64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Int64/Delta/100/ComputeAggregation-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Float64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Float64/Cumulative/100/ComputeAggregation-8 102.0 ± 0% 102.0 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Float64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Histogram/Float64/Delta/100/ComputeAggregation-8 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Int64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Int64/Cumulative/100/ComputeAggregation-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Int64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Int64/Delta/100/ComputeAggregation-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Float64/Cumulative/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Float64/Cumulative/100/ComputeAggregation-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Float64/Delta/100/Measure-8 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
Sum/Float64/Delta/100/ComputeAggregation-8 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹
geomean ² +0.00% ²
¹ all samples are equal
² summaries must be >0 to compute geomean |
cc @dashpole |
This presents a proof-of-concept for how to replace the internals of
Distinct
with a hash of the data held by aSet
.Why do this?
Currently, the
Set
andDistinct
both hold the same data: an interface with thatKeyValue
array.The Go runtime has optimized code paths for many built-in types when they are used as a map key. Our current design, using a comparable
interface{}
pointing to an array, is not as optimized as one of these built-in types.By replacing the
Distinct
type with a single hash field (i.e.~uint64
), we can realize performance imporvements without giving up any of the currentSet
orDistinct
behaviors.This is all done by computing the
Set
s data hash when it is created. This adds minimal overhead to the set creation.Given the performance costs (
NewSet
) are generally paid once for many look-ups using thatSet
(i.e. the metric SDK), and the cost is recouped in the outsized improvement in just one setting/lookup of a map, this change seems like a positive net performance improvement.Why not do this?
The hashing algorithm used to compute the
Set
data hash is the fnv-1a hash. This hash was chosen for a few reasons:The
hash/fnv
implementation is copied locally and updated to not require any allocations when it is used. This means that it can be checked using the same tests (and even verified by an independent implementation if needed).This all sounds good, but there is one big issue. This has a low collision rate, but the current implementation does not have any collision rate. Meaning by accepting these changes we will add a very small probability of invalid data setting/look-ups.