1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-11-23 22:34:47 +02:00
Files
opentelemetry-go/sdk/metric/internal/aggregate/exponential_histogram.go
David Ashpole 45539cf5a3 Only enforce cardinality limits when the attribute set does not already exist (#7422)
Fixes https://github.com/open-telemetry/opentelemetry-go/issues/7035

Benchmarks with a default value of 2000.

Parallel 
```
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/sdk/metric
cpu: Intel(R) Xeon(R) CPU @ 2.20GHz
                                                                                  │  main24.txt  │         newwithlimit24.txt          │
                                                                                  │    sec/op    │    sec/op     vs base               │
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/0-24                   272.4n ±  6%   287.6n ± 11%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/1-24                   318.1n ±  9%   289.9n ± 11%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/10-24                  305.5n ± 13%   273.4n ±  9%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/0-24                 263.4n ± 14%   273.1n ± 12%        ~ (p=0.485 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/1-24                 276.8n ±  8%   284.3n ± 10%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/10-24                266.2n ± 10%   285.9n ± 16%        ~ (p=0.394 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/0-24             265.9n ± 11%   286.4n ± 14%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/1-24             301.6n ± 16%   286.1n ±  6%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/10-24            256.1n ± 16%   282.7n ± 14%  +10.41% (p=0.041 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/0-24           269.1n ± 19%   272.0n ±  6%        ~ (p=0.485 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/1-24           299.5n ± 12%   261.6n ± 16%  -12.67% (p=0.013 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/10-24          286.3n ± 16%   275.8n ± 10%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/0-24                     265.4n ±  4%   243.1n ± 11%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/1-24                     260.6n ± 10%   258.7n ± 17%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/10-24                    266.6n ± 15%   251.8n ± 16%        ~ (p=0.699 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/0-24                   265.4n ±  9%   263.3n ± 13%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/1-24                   271.7n ± 15%   305.4n ± 21%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/10-24                  287.4n ± 13%   265.7n ±  9%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/0-24                 276.6n ±  9%   300.4n ± 25%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/1-24                 294.9n ± 11%   276.8n ± 21%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/10-24                308.3n ±  8%   281.4n ± 20%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/0-24               296.1n ± 13%   286.5n ± 14%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/1-24               329.0n ± 19%   299.6n ±  8%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/10-24              288.1n ± 28%   309.9n ± 14%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/0-24      298.3n ± 12%   282.9n ±  9%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/1-24      309.7n ±  4%   355.2n ± 11%  +14.67% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/10-24     308.5n ± 12%   307.6n ± 15%        ~ (p=1.000 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/0-24    294.1n ± 17%   289.1n ± 24%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/1-24    326.6n ± 13%   299.2n ± 14%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/10-24   329.8n ± 19%   283.7n ± 15%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/0-24                    269.9n ± 15%   266.7n ±  6%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/1-24                    297.4n ±  9%   271.9n ±  5%   -8.59% (p=0.009 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/10-24                   326.4n ± 20%   276.3n ±  5%  -15.32% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/0-24                  298.1n ± 10%   259.6n ± 10%  -12.92% (p=0.009 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/1-24                  311.4n ±  6%   276.8n ±  7%  -11.11% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/10-24                 312.4n ±  7%   264.1n ±  8%  -15.45% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/0-24              286.2n ±  8%   272.9n ± 13%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/1-24              310.1n ±  4%   289.2n ± 11%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/10-24             329.8n ±  8%   277.1n ± 11%  -15.95% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/0-24            261.4n ± 17%   262.8n ±  7%        ~ (p=0.965 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/1-24            292.3n ± 10%   271.2n ±  8%   -7.22% (p=0.026 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/10-24           306.6n ± 12%   271.9n ±  7%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/0-24                      263.2n ± 22%   254.7n ±  6%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/1-24                      284.8n ±  5%   285.0n ± 11%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/10-24                     285.7n ± 17%   296.6n ± 10%        ~ (p=0.485 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/0-24                    272.3n ± 11%   261.4n ±  8%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/1-24                    281.9n ± 21%   287.8n ± 11%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/10-24                   283.3n ± 15%   254.3n ± 12%  -10.24% (p=0.041 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/0-24                  296.1n ± 13%   295.5n ±  9%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/1-24                  310.0n ± 19%   301.7n ± 11%        ~ (p=0.485 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/10-24                 298.8n ± 17%   297.1n ±  3%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/0-24                303.4n ±  7%   280.9n ±  8%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/1-24                292.2n ± 14%   290.7n ± 13%        ~ (p=1.000 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/10-24               313.9n ± 20%   314.4n ±  8%        ~ (p=1.000 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/0-24       289.8n ±  3%   287.3n ±  3%        ~ (p=1.000 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/1-24       321.0n ±  6%   308.7n ±  8%   -3.82% (p=0.041 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/10-24      313.1n ±  9%   315.4n ±  5%        ~ (p=0.818 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/0-24     318.9n ± 13%   285.8n ± 24%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/1-24     319.0n ±  9%   317.0n ± 12%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/10-24    396.8n ± 15%   300.3n ±  5%  -24.30% (p=0.002 n=6)
geomean                                                                             294.1n         282.9n         -3.80%
```

Single-threaded:
```
goos: linux
goarch: amd64
pkg: go.opentelemetry.io/otel/sdk/metric
cpu: Intel(R) Xeon(R) CPU @ 2.20GHz
                                                                               │  main1.txt   │          newwithlimit1.txt          │
                                                                               │    sec/op    │    sec/op     vs base               │
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/0                   117.5n ±  5%   109.8n ± 20%        ~ (p=0.089 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/1                   128.5n ± 35%   118.5n ±  3%   -7.79% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Counter/Attributes/10                  126.4n ±  4%   117.3n ±  4%   -7.16% (p=0.004 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/0                 110.4n ±  1%   112.2n ±  3%   +1.59% (p=0.028 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/1                 119.8n ±  2%   118.4n ±  4%        ~ (p=0.171 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Counter/Attributes/10                120.1n ±  2%   121.9n ±  3%        ~ (p=0.394 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/0             117.3n ± 38%   109.5n ±  2%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/1             120.8n ± 28%   119.9n ±  8%        ~ (p=1.000 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64UpDownCounter/Attributes/10            121.1n ±  3%   117.0n ±  2%   -3.38% (p=0.009 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/0           111.8n ± 11%   111.0n ±  2%        ~ (p=0.331 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/1           121.7n ±  9%   117.5n ±  1%   -3.49% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64UpDownCounter/Attributes/10          126.5n ±  4%   122.3n ±  8%        ~ (p=0.290 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/0                     125.5n ± 25%   109.2n ±  3%  -12.98% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/1                     121.7n ±  6%   116.5n ±  6%   -4.31% (p=0.022 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Gauge/Attributes/10                    119.9n ±  4%   120.8n ±  4%        ~ (p=0.838 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/0                   112.5n ±  6%   109.7n ± 40%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/1                   121.2n ±  6%   121.4n ±  3%        ~ (p=0.727 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Gauge/Attributes/10                  118.6n ±  2%   120.5n ±  2%   +1.69% (p=0.045 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/0                 97.38n ± 14%   96.56n ± 10%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/1                 107.1n ±  4%   105.6n ±  5%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Int64Histogram/Attributes/10                109.7n ±  5%   108.4n ±  3%        ~ (p=0.221 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/0               98.30n ±  4%   96.02n ±  2%   -2.31% (p=0.015 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/1               107.9n ±  9%   106.2n ± 14%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsDisabled/Float64Histogram/Attributes/10              107.6n ±  1%   107.0n ±  1%        ~ (p=0.485 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/0      151.5n ±  3%   149.7n ±  2%        ~ (p=0.071 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/1      161.2n ±  2%   158.5n ±  3%   -1.67% (p=0.035 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialInt64Histogram/Attributes/10     162.3n ±  2%   159.9n ± 37%        ~ (p=0.240 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/0    154.7n ± 10%   149.0n ±  1%   -3.68% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/1    161.0n ±  2%   160.5n ±  3%        ~ (p=0.732 n=6)
SyncMeasure/NoView/ExemplarsDisabled/ExponentialFloat64Histogram/Attributes/10   162.9n ±  1%   162.6n ± 27%        ~ (p=0.838 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/0                    180.1n ±  4%   174.0n ±  7%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/1                    193.2n ± 12%   198.7n ± 10%        ~ (p=1.000 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Counter/Attributes/10                   191.4n ± 43%   183.8n ±  3%   -4.02% (p=0.004 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/0                  182.7n ±  5%   179.8n ±  4%        ~ (p=0.132 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/1                  199.4n ±  3%   185.1n ±  2%   -7.17% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Counter/Attributes/10                 198.5n ±  6%   193.0n ±  5%        ~ (p=0.589 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/0              184.7n ± 19%   173.5n ±  5%        ~ (p=0.058 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/1              183.2n ±  7%   180.7n ±  2%   -1.39% (p=0.037 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64UpDownCounter/Attributes/10             185.3n ± 21%   183.6n ±  3%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/0            187.4n ±  7%   183.8n ±  6%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/1            194.6n ±  4%   189.2n ±  2%   -2.75% (p=0.004 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64UpDownCounter/Attributes/10           199.9n ±  5%   189.4n ±  3%   -5.23% (p=0.026 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/0                      179.2n ±  5%   187.7n ±  4%        ~ (p=0.065 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/1                      188.6n ±  2%   192.1n ±  4%        ~ (p=0.221 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Gauge/Attributes/10                     186.2n ±  1%   189.9n ±  3%   +2.04% (p=0.009 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/0                    179.4n ±  4%   183.6n ±  8%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/1                    188.0n ±  2%   190.6n ±  2%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Gauge/Attributes/10                   197.1n ±  6%   188.3n ±  6%        ~ (p=0.180 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/0                  222.6n ± 10%   227.8n ±  5%        ~ (p=0.310 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/1                  224.2n ±  7%   230.8n ±  6%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Int64Histogram/Attributes/10                 240.6n ± 10%   233.6n ±  6%        ~ (p=0.394 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/0                216.9n ±  4%   222.0n ±  5%        ~ (p=0.937 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/1                223.3n ±  3%   221.4n ±  4%        ~ (p=0.372 n=6)
SyncMeasure/NoView/ExemplarsEnabled/Float64Histogram/Attributes/10               240.8n ±  5%   224.5n ± 19%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/0       224.5n ±  2%   218.2n ±  2%   -2.83% (p=0.030 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/1       230.9n ±  2%   228.8n ±  1%   -0.95% (p=0.026 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialInt64Histogram/Attributes/10      231.5n ±  4%   242.1n ±  5%        ~ (p=0.093 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/0     237.1n ±  3%   221.8n ±  6%   -6.47% (p=0.004 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/1     245.3n ±  0%   234.4n ±  4%   -4.46% (p=0.002 n=6)
SyncMeasure/NoView/ExemplarsEnabled/ExponentialFloat64Histogram/Attributes/10    249.3n ± 11%   240.9n ± 17%        ~ (p=0.589 n=6)
geomean                                                                          159.1n         156.0n         -1.97%
```

---------

Co-authored-by: Robert Pająk <pellared@hotmail.com>
2025-09-29 11:33:27 -04:00

484 lines
12 KiB
Go

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package aggregate // import "go.opentelemetry.io/otel/sdk/metric/internal/aggregate"
import (
"context"
"errors"
"math"
"sync"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
)
const (
expoMaxScale = 20
expoMinScale = -10
smallestNonZeroNormalFloat64 = 0x1p-1022
// These redefine the Math constants with a type, so the compiler won't coerce
// them into an int on 32 bit platforms.
maxInt64 int64 = math.MaxInt64
minInt64 int64 = math.MinInt64
)
// expoHistogramDataPoint is a single data point in an exponential histogram.
type expoHistogramDataPoint[N int64 | float64] struct {
attrs attribute.Set
res FilteredExemplarReservoir[N]
count uint64
min N
max N
sum N
maxSize int
noMinMax bool
noSum bool
scale int32
posBuckets expoBuckets
negBuckets expoBuckets
zeroCount uint64
}
func newExpoHistogramDataPoint[N int64 | float64](
attrs attribute.Set,
maxSize int,
maxScale int32,
noMinMax, noSum bool,
) *expoHistogramDataPoint[N] { // nolint:revive // we need this control flag
f := math.MaxFloat64
ma := N(f) // if N is int64, max will overflow to -9223372036854775808
mi := N(-f)
if N(maxInt64) > N(f) {
ma = N(maxInt64)
mi = N(minInt64)
}
return &expoHistogramDataPoint[N]{
attrs: attrs,
min: ma,
max: mi,
maxSize: maxSize,
noMinMax: noMinMax,
noSum: noSum,
scale: maxScale,
}
}
// record adds a new measurement to the histogram. It will rescale the buckets if needed.
func (p *expoHistogramDataPoint[N]) record(v N) {
p.count++
if !p.noMinMax {
if v < p.min {
p.min = v
}
if v > p.max {
p.max = v
}
}
if !p.noSum {
p.sum += v
}
absV := math.Abs(float64(v))
if float64(absV) == 0.0 {
p.zeroCount++
return
}
bin := p.getBin(absV)
bucket := &p.posBuckets
if v < 0 {
bucket = &p.negBuckets
}
// If the new bin would make the counts larger than maxScale, we need to
// downscale current measurements.
if scaleDelta := p.scaleChange(bin, bucket.startBin, len(bucket.counts)); scaleDelta > 0 {
if p.scale-scaleDelta < expoMinScale {
// With a scale of -10 there is only two buckets for the whole range of float64 values.
// This can only happen if there is a max size of 1.
otel.Handle(errors.New("exponential histogram scale underflow"))
return
}
// Downscale
p.scale -= scaleDelta
p.posBuckets.downscale(scaleDelta)
p.negBuckets.downscale(scaleDelta)
bin = p.getBin(absV)
}
bucket.record(bin)
}
// getBin returns the bin v should be recorded into.
func (p *expoHistogramDataPoint[N]) getBin(v float64) int32 {
frac, expInt := math.Frexp(v)
// 11-bit exponential.
exp := int32(expInt) // nolint: gosec
if p.scale <= 0 {
// Because of the choice of fraction is always 1 power of two higher than we want.
var correction int32 = 1
if frac == .5 {
// If v is an exact power of two the frac will be .5 and the exp
// will be one higher than we want.
correction = 2
}
return (exp - correction) >> (-p.scale)
}
return exp<<p.scale + int32(math.Log(frac)*scaleFactors[p.scale]) - 1
}
// scaleFactors are constants used in calculating the logarithm index. They are
// equivalent to 2^index/log(2).
var scaleFactors = [21]float64{
math.Ldexp(math.Log2E, 0),
math.Ldexp(math.Log2E, 1),
math.Ldexp(math.Log2E, 2),
math.Ldexp(math.Log2E, 3),
math.Ldexp(math.Log2E, 4),
math.Ldexp(math.Log2E, 5),
math.Ldexp(math.Log2E, 6),
math.Ldexp(math.Log2E, 7),
math.Ldexp(math.Log2E, 8),
math.Ldexp(math.Log2E, 9),
math.Ldexp(math.Log2E, 10),
math.Ldexp(math.Log2E, 11),
math.Ldexp(math.Log2E, 12),
math.Ldexp(math.Log2E, 13),
math.Ldexp(math.Log2E, 14),
math.Ldexp(math.Log2E, 15),
math.Ldexp(math.Log2E, 16),
math.Ldexp(math.Log2E, 17),
math.Ldexp(math.Log2E, 18),
math.Ldexp(math.Log2E, 19),
math.Ldexp(math.Log2E, 20),
}
// scaleChange returns the magnitude of the scale change needed to fit bin in
// the bucket. If no scale change is needed 0 is returned.
func (p *expoHistogramDataPoint[N]) scaleChange(bin, startBin int32, length int) int32 {
if length == 0 {
// No need to rescale if there are no buckets.
return 0
}
low := int(startBin)
high := int(bin)
if startBin >= bin {
low = int(bin)
high = int(startBin) + length - 1
}
var count int32
for high-low >= p.maxSize {
low >>= 1
high >>= 1
count++
if count > expoMaxScale-expoMinScale {
return count
}
}
return count
}
// expoBuckets is a set of buckets in an exponential histogram.
type expoBuckets struct {
startBin int32
counts []uint64
}
// record increments the count for the given bin, and expands the buckets if needed.
// Size changes must be done before calling this function.
func (b *expoBuckets) record(bin int32) {
if len(b.counts) == 0 {
b.counts = []uint64{1}
b.startBin = bin
return
}
endBin := int(b.startBin) + len(b.counts) - 1
// if the new bin is inside the current range
if bin >= b.startBin && int(bin) <= endBin {
b.counts[bin-b.startBin]++
return
}
// if the new bin is before the current start add spaces to the counts
if bin < b.startBin {
origLen := len(b.counts)
newLength := endBin - int(bin) + 1
shift := b.startBin - bin
if newLength > cap(b.counts) {
b.counts = append(b.counts, make([]uint64, newLength-len(b.counts))...)
}
copy(b.counts[shift:origLen+int(shift)], b.counts)
b.counts = b.counts[:newLength]
for i := 1; i < int(shift); i++ {
b.counts[i] = 0
}
b.startBin = bin
b.counts[0] = 1
return
}
// if the new is after the end add spaces to the end
if int(bin) > endBin {
if int(bin-b.startBin) < cap(b.counts) {
b.counts = b.counts[:bin-b.startBin+1]
for i := endBin + 1 - int(b.startBin); i < len(b.counts); i++ {
b.counts[i] = 0
}
b.counts[bin-b.startBin] = 1
return
}
end := make([]uint64, int(bin-b.startBin)-len(b.counts)+1)
b.counts = append(b.counts, end...)
b.counts[bin-b.startBin] = 1
}
}
// downscale shrinks a bucket by a factor of 2*s. It will sum counts into the
// correct lower resolution bucket.
func (b *expoBuckets) downscale(delta int32) {
// Example
// delta = 2
// Original offset: -6
// Counts: [ 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// bins: -6 -5, -4, -3, -2, -1, 0, 1, 2, 3, 4
// new bins:-2, -2, -1, -1, -1, -1, 0, 0, 0, 0, 1
// new Offset: -2
// new Counts: [4, 14, 30, 10]
if len(b.counts) <= 1 || delta < 1 {
b.startBin >>= delta
return
}
steps := int32(1) << delta
offset := b.startBin % steps
offset = (offset + steps) % steps // to make offset positive
for i := 1; i < len(b.counts); i++ {
idx := i + int(offset)
if idx%int(steps) == 0 {
b.counts[idx/int(steps)] = b.counts[i]
continue
}
b.counts[idx/int(steps)] += b.counts[i]
}
lastIdx := (len(b.counts) - 1 + int(offset)) / int(steps)
b.counts = b.counts[:lastIdx+1]
b.startBin >>= delta
}
// newExponentialHistogram returns an Aggregator that summarizes a set of
// measurements as an exponential histogram. Each histogram is scoped by attributes
// and the aggregation cycle the measurements were made in.
func newExponentialHistogram[N int64 | float64](
maxSize, maxScale int32,
noMinMax, noSum bool,
limit int,
r func(attribute.Set) FilteredExemplarReservoir[N],
) *expoHistogram[N] {
return &expoHistogram[N]{
noSum: noSum,
noMinMax: noMinMax,
maxSize: int(maxSize),
maxScale: maxScale,
newRes: r,
limit: newLimiter[*expoHistogramDataPoint[N]](limit),
values: make(map[attribute.Distinct]*expoHistogramDataPoint[N]),
start: now(),
}
}
// expoHistogram summarizes a set of measurements as an histogram with exponentially
// defined buckets.
type expoHistogram[N int64 | float64] struct {
noSum bool
noMinMax bool
maxSize int
maxScale int32
newRes func(attribute.Set) FilteredExemplarReservoir[N]
limit limiter[*expoHistogramDataPoint[N]]
values map[attribute.Distinct]*expoHistogramDataPoint[N]
valuesMu sync.Mutex
start time.Time
}
func (e *expoHistogram[N]) measure(
ctx context.Context,
value N,
fltrAttr attribute.Set,
droppedAttr []attribute.KeyValue,
) {
// Ignore NaN and infinity.
if math.IsInf(float64(value), 0) || math.IsNaN(float64(value)) {
return
}
e.valuesMu.Lock()
defer e.valuesMu.Unlock()
v, ok := e.values[fltrAttr.Equivalent()]
if !ok {
fltrAttr = e.limit.Attributes(fltrAttr, e.values)
// If we overflowed, make sure we add to the existing overflow series
// if it already exists.
v, ok = e.values[fltrAttr.Equivalent()]
if !ok {
v = newExpoHistogramDataPoint[N](fltrAttr, e.maxSize, e.maxScale, e.noMinMax, e.noSum)
v.res = e.newRes(fltrAttr)
e.values[fltrAttr.Equivalent()] = v
}
}
v.record(value)
v.res.Offer(ctx, value, droppedAttr)
}
func (e *expoHistogram[N]) delta(
dest *metricdata.Aggregation, //nolint:gocritic // The pointer is needed for the ComputeAggregation interface
) int {
t := now()
// If *dest is not a metricdata.ExponentialHistogram, memory reuse is missed.
// In that case, use the zero-value h and hope for better alignment next cycle.
h, _ := (*dest).(metricdata.ExponentialHistogram[N])
h.Temporality = metricdata.DeltaTemporality
e.valuesMu.Lock()
defer e.valuesMu.Unlock()
n := len(e.values)
hDPts := reset(h.DataPoints, n, n)
var i int
for _, val := range e.values {
hDPts[i].Attributes = val.attrs
hDPts[i].StartTime = e.start
hDPts[i].Time = t
hDPts[i].Count = val.count
hDPts[i].Scale = val.scale
hDPts[i].ZeroCount = val.zeroCount
hDPts[i].ZeroThreshold = 0.0
hDPts[i].PositiveBucket.Offset = val.posBuckets.startBin
hDPts[i].PositiveBucket.Counts = reset(
hDPts[i].PositiveBucket.Counts,
len(val.posBuckets.counts),
len(val.posBuckets.counts),
)
copy(hDPts[i].PositiveBucket.Counts, val.posBuckets.counts)
hDPts[i].NegativeBucket.Offset = val.negBuckets.startBin
hDPts[i].NegativeBucket.Counts = reset(
hDPts[i].NegativeBucket.Counts,
len(val.negBuckets.counts),
len(val.negBuckets.counts),
)
copy(hDPts[i].NegativeBucket.Counts, val.negBuckets.counts)
if !e.noSum {
hDPts[i].Sum = val.sum
}
if !e.noMinMax {
hDPts[i].Min = metricdata.NewExtrema(val.min)
hDPts[i].Max = metricdata.NewExtrema(val.max)
}
collectExemplars(&hDPts[i].Exemplars, val.res.Collect)
i++
}
// Unused attribute sets do not report.
clear(e.values)
e.start = t
h.DataPoints = hDPts
*dest = h
return n
}
func (e *expoHistogram[N]) cumulative(
dest *metricdata.Aggregation, //nolint:gocritic // The pointer is needed for the ComputeAggregation interface
) int {
t := now()
// If *dest is not a metricdata.ExponentialHistogram, memory reuse is missed.
// In that case, use the zero-value h and hope for better alignment next cycle.
h, _ := (*dest).(metricdata.ExponentialHistogram[N])
h.Temporality = metricdata.CumulativeTemporality
e.valuesMu.Lock()
defer e.valuesMu.Unlock()
n := len(e.values)
hDPts := reset(h.DataPoints, n, n)
var i int
for _, val := range e.values {
hDPts[i].Attributes = val.attrs
hDPts[i].StartTime = e.start
hDPts[i].Time = t
hDPts[i].Count = val.count
hDPts[i].Scale = val.scale
hDPts[i].ZeroCount = val.zeroCount
hDPts[i].ZeroThreshold = 0.0
hDPts[i].PositiveBucket.Offset = val.posBuckets.startBin
hDPts[i].PositiveBucket.Counts = reset(
hDPts[i].PositiveBucket.Counts,
len(val.posBuckets.counts),
len(val.posBuckets.counts),
)
copy(hDPts[i].PositiveBucket.Counts, val.posBuckets.counts)
hDPts[i].NegativeBucket.Offset = val.negBuckets.startBin
hDPts[i].NegativeBucket.Counts = reset(
hDPts[i].NegativeBucket.Counts,
len(val.negBuckets.counts),
len(val.negBuckets.counts),
)
copy(hDPts[i].NegativeBucket.Counts, val.negBuckets.counts)
if !e.noSum {
hDPts[i].Sum = val.sum
}
if !e.noMinMax {
hDPts[i].Min = metricdata.NewExtrema(val.min)
hDPts[i].Max = metricdata.NewExtrema(val.max)
}
collectExemplars(&hDPts[i].Exemplars, val.res.Collect)
i++
// TODO (#3006): This will use an unbounded amount of memory if there
// are unbounded number of attribute sets being aggregated. Attribute
// sets that become "stale" need to be forgotten so this will not
// overload the system.
}
h.DataPoints = hDPts
*dest = h
return n
}