1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-10-31 00:07:40 +02:00

Allow optimizing locking for built-in exemplar reservoirs (#7423)

Fixes https://github.com/open-telemetry/opentelemetry-go/issues/7388

Benchmarks seem like mostly noise. It isn't actually necessary to lock
in the exemplar reservoir today because of our SDK design, but this
allows us to make optimizations in the future. After
https://github.com/open-telemetry/opentelemetry-go/pull/7427,
improvements to exemplar reservoir locking will greatly improve the
ExemplarEnabled benchmarks.

Parallel benchmarks:
```
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/sdk/metric
cpu: Intel(R) Xeon(R) CPU @ 2.20GHz
                                                                                  │  main24.txt  │           exemplar24.txt            │
                                                                                  │    sec/op    │    sec/op     vs base               │
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/0-24                   399.5n ± 16%   367.8n ± 17%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/1-24                   369.4n ± 27%   410.2n ± 11%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/10-24                  372.6n ± 23%   398.9n ±  8%        ~ (p=0.394 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/0-24                 313.4n ± 12%   357.7n ± 20%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/1-24                 389.9n ± 12%   379.5n ±  9%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/10-24                441.4n ± 13%   359.1n ± 18%  -18.64% (p=0.009 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/0-24             415.8n ± 22%   400.3n ± 16%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/1-24             346.9n ±  8%   364.6n ± 19%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/10-24            358.9n ± 14%   407.1n ± 17%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/0-24           381.9n ± 27%   375.2n ± 10%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/1-24           361.9n ± 19%   389.6n ± 23%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/10-24          356.0n ±  8%   416.1n ± 14%  +16.90% (p=0.015 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/0-24                     313.9n ± 11%   385.7n ± 19%  +22.88% (p=0.041 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/1-24                     368.8n ± 18%   387.6n ± 17%        ~ (p=0.394 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/10-24                    346.1n ± 40%   460.3n ± 16%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/0-24                   325.9n ± 10%   357.8n ± 19%   +9.77% (p=0.026 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/1-24                   372.1n ± 18%   395.2n ± 14%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/10-24                  353.5n ± 23%   416.0n ± 15%  +17.66% (p=0.024 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/0-24                 351.8n ± 15%   362.7n ±  7%        ~ (p=0.699 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/1-24                 378.8n ± 17%   413.4n ± 13%        ~ (p=0.288 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/10-24                361.5n ± 13%   418.2n ± 14%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/0-24               305.0n ± 21%   361.9n ± 13%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/1-24               411.1n ± 12%   403.9n ±  9%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/10-24              353.4n ± 39%   380.9n ± 17%        ~ (p=0.394 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/0-24      457.5n ± 33%   454.2n ± 13%        ~ (p=1.000 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/1-24      436.6n ± 23%   459.0n ± 10%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/10-24     383.3n ± 22%   461.9n ± 12%  +20.51% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/0-24    371.9n ± 14%   421.4n ± 19%  +13.33% (p=0.004 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/1-24    433.7n ± 20%   490.8n ± 10%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/10-24   433.2n ± 18%   511.4n ±  9%  +18.05% (p=0.041 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/0-24                    477.5n ± 14%   384.6n ±  7%  -19.47% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/1-24                    481.1n ± 17%   430.0n ± 18%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/10-24                   425.1n ± 27%   436.3n ± 12%        ~ (p=0.699 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/0-24                  394.5n ±  8%   415.5n ± 15%        ~ (p=0.589 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/1-24                  434.4n ± 13%   440.8n ± 15%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/10-24                 481.3n ± 19%   404.1n ± 14%  -16.05% (p=0.009 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/0-24              364.9n ± 29%   424.3n ±  8%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/1-24              401.2n ± 24%   482.2n ± 12%  +20.20% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/10-24             438.6n ± 19%   404.8n ± 18%        ~ (p=0.485 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/0-24            392.7n ± 17%   427.7n ± 25%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/1-24            392.6n ±  5%   388.5n ±  7%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/10-24           401.3n ± 19%   409.7n ±  8%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/0-24                      369.8n ± 15%   374.2n ± 17%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/1-24                      359.4n ± 13%   387.1n ± 16%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/10-24                     393.2n ± 18%   450.0n ± 10%  +14.43% (p=0.015 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/0-24                    399.8n ± 23%   361.2n ± 11%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/1-24                    439.4n ± 25%   412.0n ± 10%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/10-24                   401.7n ± 17%   380.0n ± 11%        ~ (p=0.394 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/0-24                  508.8n ± 18%   532.6n ± 16%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/1-24                  505.9n ± 22%   494.8n ± 14%        ~ (p=0.589 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/10-24                 597.8n ± 10%   490.5n ± 23%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/0-24                566.2n ± 21%   482.9n ± 10%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/1-24                440.2n ±  9%   549.3n ±  8%  +24.77% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/10-24               436.3n ± 16%   530.8n ± 19%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/0-24       395.8n ± 25%   441.6n ±  9%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/1-24       440.2n ±  9%   455.2n ±  7%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/10-24      415.7n ± 12%   527.5n ±  9%  +26.91% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/0-24     376.1n ± 19%   461.9n ± 13%  +22.81% (p=0.009 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/1-24     383.6n ± 10%   422.7n ± 26%  +10.21% (p=0.015 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/10-24    398.5n ±  9%   538.5n ±  7%  +35.13% (p=0.002 n=6)
geomean                                                                             399.4n         422.3n         +5.72%
```

Single-threaded benchmarks:
```
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/sdk/metric
cpu: Intel(R) Xeon(R) CPU @ 2.20GHz
                                                                               │  main1.txt   │            exemplar1.txt            │
                                                                               │    sec/op    │    sec/op     vs base               │
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/0                   180.1n ± 21%   156.2n ± 10%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/1                   168.4n ±  9%   179.2n ± 11%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/10                  164.0n ± 10%   199.0n ± 20%  +21.30% (p=0.004 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/0                 153.9n ±  6%   170.1n ±  2%  +10.53% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/1                 178.0n ±  5%   178.5n ±  6%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/10                175.8n ±  7%   165.3n ± 18%        ~ (p=0.589 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/0             152.0n ±  8%   163.1n ± 26%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/1             168.1n ±  8%   168.0n ± 14%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/10            167.4n ±  4%   164.3n ± 12%        ~ (p=0.699 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/0           151.7n ± 17%   156.0n ± 28%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/1           173.6n ±  5%   169.3n ±  5%   -2.45% (p=0.041 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/10          169.3n ±  4%   165.7n ±  7%        ~ (p=0.394 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/0                     155.5n ± 15%   153.8n ± 11%        ~ (p=0.558 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/1                     166.7n ±  3%   173.5n ±  7%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/10                    174.4n ± 17%   167.6n ± 13%        ~ (p=0.699 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/0                   180.1n ± 32%   154.8n ±  5%  -14.02% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/1                   204.6n ± 23%   173.8n ± 19%        ~ (p=0.069 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/10                  226.6n ± 28%   174.5n ± 10%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/0                 132.7n ± 13%   140.7n ± 14%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/1                 143.4n ±  9%   162.6n ±  9%  +13.42% (p=0.004 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/10                154.7n ±  7%   172.3n ±  6%  +11.38% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/0               136.8n ± 10%   145.5n ± 14%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/1               148.4n ± 11%   159.7n ±  8%        ~ (p=0.167 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/10              193.0n ± 25%   165.5n ±  9%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/0      249.8n ± 47%   229.5n ±  7%        ~ (p=0.394 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/1      262.1n ± 20%   245.1n ± 12%        ~ (p=0.699 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/10     285.4n ± 20%   249.9n ± 17%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/0    272.9n ± 20%   216.3n ±  6%  -20.75% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/1    311.8n ± 29%   234.4n ±  6%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/10   228.2n ±  6%   234.2n ±  4%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/0                    289.2n ± 39%   263.3n ± 10%        ~ (p=1.000 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/1                    271.9n ± 18%   280.6n ±  8%        ~ (p=0.589 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/10                   272.1n ±  6%   303.7n ± 14%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/0                  280.1n ±  8%   268.8n ±  5%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/1                  291.6n ± 81%   268.8n ±  7%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/10                 276.3n ± 13%   278.1n ±  6%        ~ (p=0.784 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/0              254.7n ±  7%   282.4n ±  5%  +10.85% (p=0.026 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/1              277.5n ± 11%   285.2n ± 16%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/10             267.0n ± 10%   275.9n ±  5%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/0            256.9n ±  3%   286.4n ±  8%  +11.46% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/1            267.9n ± 16%   278.0n ± 12%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/10           272.5n ±  4%   267.4n ±  6%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/0                      272.5n ± 22%   266.2n ± 14%        ~ (p=0.589 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/1                      355.2n ± 11%   275.2n ±  7%  -22.52% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/10                     281.3n ±  7%   268.9n ±  4%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/0                    257.0n ±  9%   308.9n ±  7%  +20.20% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/1                    265.9n ±  9%   319.6n ±  7%  +20.18% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/10                   279.9n ±  3%   332.9n ±  7%  +18.92% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/0                  313.9n ± 11%   345.1n ± 10%   +9.91% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/1                  332.1n ±  7%   359.9n ±  9%   +8.37% (p=0.009 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/10                 366.4n ± 55%   380.1n ±  4%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/0                336.8n ± 12%   346.3n ±  7%        ~ (p=0.589 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/1                361.9n ± 18%   375.0n ±  8%   +3.62% (p=0.004 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/10               351.8n ±  6%   392.1n ±  4%  +11.46% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/0       358.8n ±  9%   344.3n ±  5%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/1       352.0n ±  7%   331.5n ±  5%   -5.84% (p=0.041 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/10      373.5n ± 10%   353.7n ±  9%        ~ (p=0.394 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/0     328.5n ±  9%   345.4n ±  5%   +5.14% (p=0.015 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/1     349.1n ± 28%   337.8n ±  9%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/10    347.5n ± 10%   344.2n ± 10%        ~ (p=1.000 n=6)
geomean                                                                          235.3n         234.4n         -0.38%
```
This commit is contained in:
David Ashpole
2025-10-01 12:32:00 -04:00
committed by GitHub
parent 6c54ef68e5
commit ea38204f20
8 changed files with 175 additions and 21 deletions

View File

@@ -10,6 +10,7 @@ import (
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/metric/internal/reservoir"
)
// FixedSizeReservoirProvider returns a provider of [FixedSizeReservoir].
@@ -34,6 +35,7 @@ var _ Reservoir = &FixedSizeReservoir{}
// If there are more than k, the Reservoir will then randomly sample all
// additional measurement with a decreasing probability.
type FixedSizeReservoir struct {
reservoir.ConcurrentSafe
*storage
// count is the number of measurement seen.
@@ -123,12 +125,12 @@ func (r *FixedSizeReservoir) Offer(ctx context.Context, t time.Time, n Value, a
// https://github.com/MrAlias/reservoir-sampling for a performance
// comparison of reservoir sampling algorithms.
if int(r.count) < cap(r.store) {
r.store[r.count] = newMeasurement(ctx, t, n, a)
if int(r.count) < cap(r.measurements) {
r.store(int(r.count), newMeasurement(ctx, t, n, a))
} else if r.count == r.next {
// Overwrite a random existing measurement with the one offered.
idx := int(rand.Int64N(int64(cap(r.store))))
r.store[idx] = newMeasurement(ctx, t, n, a)
idx := int(rand.Int64N(int64(cap(r.measurements))))
r.store(idx, newMeasurement(ctx, t, n, a))
r.advance()
}
r.count++
@@ -139,7 +141,7 @@ func (r *FixedSizeReservoir) reset() {
// This resets the number of exemplars known.
r.count = 0
// Random index inserts should only happen after the storage is full.
r.next = int64(cap(r.store))
r.next = int64(cap(r.measurements))
// Initial random number in the series used to generate r.next.
//
@@ -150,7 +152,7 @@ func (r *FixedSizeReservoir) reset() {
// This maps the uniform random number in (0,1) to a geometric distribution
// over the same interval. The mean of the distribution is inversely
// proportional to the storage capacity.
r.w = math.Exp(math.Log(r.randomFloat64()) / float64(cap(r.store)))
r.w = math.Exp(math.Log(r.randomFloat64()) / float64(cap(r.measurements)))
r.advance()
}
@@ -170,7 +172,7 @@ func (r *FixedSizeReservoir) advance() {
// therefore the next r.w will be based on the same distribution (i.e.
// `max(u_1,u_2,...,u_k)`). Therefore, we can sample the next r.w by
// computing the next random number `u` and take r.w as `w * u^(1/k)`.
r.w *= math.Exp(math.Log(r.randomFloat64()) / float64(cap(r.store)))
r.w *= math.Exp(math.Log(r.randomFloat64()) / float64(cap(r.measurements)))
// Use the new random number in the series to calculate the count of the
// next measurement that will be stored.
//

View File

@@ -45,7 +45,7 @@ func TestNewFixedSizeReservoirSamplingCorrectness(t *testing.T) {
}
var sum float64
for _, m := range r.store {
for _, m := range r.measurements {
sum += m.Value.Float64()
}
mean := sum / float64(sampleSize)

View File

@@ -10,6 +10,7 @@ import (
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/metric/internal/reservoir"
)
// HistogramReservoirProvider is a provider of [HistogramReservoir].
@@ -39,6 +40,7 @@ var _ Reservoir = &HistogramReservoir{}
// falls within a histogram bucket. The histogram bucket upper-boundaries are
// define by bounds.
type HistogramReservoir struct {
reservoir.ConcurrentSafe
*storage
// bounds are bucket bounds in ascending order.
@@ -57,14 +59,14 @@ type HistogramReservoir struct {
// parameters are the value and dropped (filtered) attributes of the
// measurement respectively.
func (r *HistogramReservoir) Offer(ctx context.Context, t time.Time, v Value, a []attribute.KeyValue) {
var x float64
var n float64
switch v.Type() {
case Int64ValueType:
x = float64(v.Int64())
n = float64(v.Int64())
case Float64ValueType:
x = v.Float64()
n = v.Float64()
default:
panic("unknown value type")
}
r.store[sort.SearchFloat64s(r.bounds, x)] = newMeasurement(ctx, t, v, a)
r.store(sort.SearchFloat64s(r.bounds, n), newMeasurement(ctx, t, v, a))
}

View File

@@ -5,6 +5,7 @@ package exemplar // import "go.opentelemetry.io/otel/sdk/metric/exemplar"
import (
"context"
"sync"
"time"
"go.opentelemetry.io/otel/attribute"
@@ -13,24 +14,33 @@ import (
// storage is an exemplar storage for [Reservoir] implementations.
type storage struct {
// store are the measurements sampled.
mu sync.Mutex
// measurements are the measurements sampled.
//
// This does not use []metricdata.Exemplar because it potentially would
// require an allocation for trace and span IDs in the hot path of Offer.
store []measurement
measurements []measurement
}
func newStorage(n int) *storage {
return &storage{store: make([]measurement, n)}
return &storage{measurements: make([]measurement, n)}
}
func (r *storage) store(idx int, m measurement) {
r.mu.Lock()
defer r.mu.Unlock()
r.measurements[idx] = m
}
// Collect returns all the held exemplars.
//
// The Reservoir state is preserved after this call.
func (r *storage) Collect(dest *[]Exemplar) {
*dest = reset(*dest, len(r.store), len(r.store))
r.mu.Lock()
defer r.mu.Unlock()
*dest = reset(*dest, len(r.measurements), len(r.measurements))
var n int
for _, m := range r.store {
for _, m := range r.measurements {
if !m.valid {
continue
}

View File

@@ -5,10 +5,12 @@ package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggreg
import (
"context"
"sync"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/metric/exemplar"
"go.opentelemetry.io/otel/sdk/metric/internal/reservoir"
)
// FilteredExemplarReservoir wraps a [exemplar.Reservoir] with a filter.
@@ -29,6 +31,11 @@ type FilteredExemplarReservoir[N int64 | float64] interface {
type filteredExemplarReservoir[N int64 | float64] struct {
filter exemplar.Filter
reservoir exemplar.Reservoir
// The exemplar.Reservoir is not required to be concurrent safe, but
// implementations can indicate that they are concurrent-safe by embedding
// reservoir.ConcurrentSafe in order to improve performance.
reservoirMux sync.Mutex
concurrentSafe bool
}
// NewFilteredExemplarReservoir creates a [FilteredExemplarReservoir] which only offers values
@@ -37,17 +44,30 @@ func NewFilteredExemplarReservoir[N int64 | float64](
f exemplar.Filter,
r exemplar.Reservoir,
) FilteredExemplarReservoir[N] {
_, concurrentSafe := r.(reservoir.ConcurrentSafe)
return &filteredExemplarReservoir[N]{
filter: f,
reservoir: r,
filter: f,
reservoir: r,
concurrentSafe: concurrentSafe,
}
}
func (f *filteredExemplarReservoir[N]) Offer(ctx context.Context, val N, attr []attribute.KeyValue) {
if f.filter(ctx) {
ts := time.Now()
if !f.concurrentSafe {
f.reservoirMux.Lock()
defer f.reservoirMux.Unlock()
}
// only record the current time if we are sampling this measurement.
f.reservoir.Offer(ctx, time.Now(), exemplar.NewValue(val), attr)
f.reservoir.Offer(ctx, ts, exemplar.NewValue(val), attr)
}
}
func (f *filteredExemplarReservoir[N]) Collect(dest *[]exemplar.Exemplar) { f.reservoir.Collect(dest) }
func (f *filteredExemplarReservoir[N]) Collect(dest *[]exemplar.Exemplar) {
if !f.concurrentSafe {
f.reservoirMux.Lock()
defer f.reservoirMux.Unlock()
}
f.reservoir.Collect(dest)
}

View File

@@ -0,0 +1,103 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
import (
"context"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/metric/exemplar"
"go.opentelemetry.io/otel/sdk/metric/internal/reservoir"
)
func TestConcurrentSafeFilteredReservoir(t *testing.T) {
for _, tc := range []struct {
desc string
reservoir exemplar.Reservoir
expectConcurrentSafe bool
}{
{
desc: "concurrent safe",
reservoir: &concurrentSafeReservoir{},
expectConcurrentSafe: true,
},
{
desc: "not concurrent safe",
reservoir: &notConcurrentSafeReservoir{},
expectConcurrentSafe: false,
},
} {
t.Run(tc.desc, func(t *testing.T) {
reservoir := NewFilteredExemplarReservoir[int64](exemplar.AlwaysOnFilter, tc.reservoir)
var wg sync.WaitGroup
for range 5 {
wg.Add(1)
go func() {
reservoir.Offer(t.Context(), 25, []attribute.KeyValue{})
wg.Done()
}()
}
into := []exemplar.Exemplar{}
for range 2 {
reservoir.Collect(&into)
}
wg.Wait()
assert.Len(t, into, 1)
assert.Equal(t, reservoir.(*filteredExemplarReservoir[int64]).concurrentSafe, tc.expectConcurrentSafe)
})
}
}
type notConcurrentSafeReservoir struct {
ex exemplar.Exemplar
}
func (r *notConcurrentSafeReservoir) Offer(
_ context.Context,
t time.Time,
val exemplar.Value,
attr []attribute.KeyValue,
) {
r.ex = exemplar.Exemplar{
FilteredAttributes: attr,
Time: t,
Value: val,
}
}
func (r *notConcurrentSafeReservoir) Collect(dest *[]exemplar.Exemplar) {
*dest = make([]exemplar.Exemplar, 1)
(*dest)[0].FilteredAttributes = r.ex.FilteredAttributes
(*dest)[0].Time = r.ex.Time
(*dest)[0].Value = r.ex.Value
*dest = (*dest)[:1]
}
type concurrentSafeReservoir struct {
base notConcurrentSafeReservoir
sync.Mutex
reservoir.ConcurrentSafe
}
func (r *concurrentSafeReservoir) Offer(
ctx context.Context,
t time.Time,
val exemplar.Value,
attr []attribute.KeyValue,
) {
r.Lock()
defer r.Unlock()
r.base.Offer(ctx, t, val, attr)
}
func (r *concurrentSafeReservoir) Collect(dest *[]exemplar.Exemplar) {
r.Lock()
defer r.Unlock()
r.base.Collect(dest)
}

View File

@@ -0,0 +1,11 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package reservoir // import "go.opentelemetry.io/otel/sdk/metric/internal/reservoir"
// ConcurrentSafe is an interface that can be embedded in an
// exemplar.Reservoir to indicate to the SDK that it is safe to invoke its
// methods concurrently. If this interface is not embedded, the SDK assumes it
// is not safe to call concurrently and locks around Reservoir methods. This
// is currently only used by the built-in reservoirs.
type ConcurrentSafe interface{ concurrentSafe() }

View File

@@ -0,0 +1,6 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package reservoir contains experimental features used by built-in exemplar
// reservoirs which require coordination with the metrics SDK.
package reservoir // import "go.opentelemetry.io/otel/sdk/metric/internal/reservoir"