You've already forked opentelemetry-go
							
							
				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:
		| @@ -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. | ||||
| 	// | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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)) | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| 		} | ||||
|   | ||||
| @@ -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) | ||||
| } | ||||
|   | ||||
							
								
								
									
										103
									
								
								sdk/metric/internal/aggregate/filtered_reservoir_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								sdk/metric/internal/aggregate/filtered_reservoir_test.go
									
									
									
									
									
										Normal 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:            ¬ConcurrentSafeReservoir{}, | ||||
| 			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) | ||||
| } | ||||
							
								
								
									
										11
									
								
								sdk/metric/internal/reservoir/concurrent_safe.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								sdk/metric/internal/reservoir/concurrent_safe.go
									
									
									
									
									
										Normal 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() } | ||||
							
								
								
									
										6
									
								
								sdk/metric/internal/reservoir/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								sdk/metric/internal/reservoir/doc.go
									
									
									
									
									
										Normal 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" | ||||
		Reference in New Issue
	
	Block a user