Skip to content

Commit 8d1cad2

Browse files
asafmjack-berg
andauthored
Memory mode: Adding support for synchronous instruments - explicit histogram (open-telemetry#6153)
Co-authored-by: jack-berg <34418638+jack-berg@users.noreply.github.com>
1 parent 737dfef commit 8d1cad2

13 files changed

Lines changed: 659 additions & 55 deletions

File tree

sdk/common/src/main/java/io/opentelemetry/sdk/internal/DynamicPrimitiveLongList.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ public static DynamicPrimitiveLongList of(long... values) {
6565
return list;
6666
}
6767

68+
public static DynamicPrimitiveLongList ofSubArrayCapacity(int subarrayCapacity) {
69+
return new DynamicPrimitiveLongList(subarrayCapacity);
70+
}
71+
6872
public static DynamicPrimitiveLongList empty() {
6973
return new DynamicPrimitiveLongList();
7074
}

sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ public enum HistogramAggregationParam {
1717
new DoubleExplicitBucketHistogramAggregator(
1818
ExplicitBucketHistogramUtils.createBoundaryArray(
1919
ExplicitBucketHistogramUtils.DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES),
20-
ExemplarReservoir::doubleNoSamples)),
20+
ExemplarReservoir::doubleNoSamples,
21+
IMMUTABLE_DATA)),
2122
EXPLICIT_SINGLE_BUCKET(
2223
new DoubleExplicitBucketHistogramAggregator(
2324
ExplicitBucketHistogramUtils.createBoundaryArray(Collections.emptyList()),
24-
ExemplarReservoir::doubleNoSamples)),
25+
ExemplarReservoir::doubleNoSamples,
26+
IMMUTABLE_DATA)),
2527
EXPONENTIAL_SMALL_CIRCULAR_BUFFER(
2628
new DoubleBase2ExponentialHistogramAggregator(
2729
ExemplarReservoir::doubleNoSamples, 20, 0, IMMUTABLE_DATA)),

sdk/metrics/src/jmhBasedTest/java/io/opentelemetry/sdk/metrics/internal/state/ProfileBenchmark.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static void main(String[] args) {
3737
// Parameters
3838
AggregationTemporality aggregationTemporality = AggregationTemporality.DELTA;
3939
MemoryMode memoryMode = MemoryMode.REUSABLE_DATA;
40-
TestInstrumentType testInstrumentType = TestInstrumentType.EXPONENTIAL_HISTOGRAM;
40+
TestInstrumentType testInstrumentType = TestInstrumentType.EXPLICIT_BUCKET;
4141

4242
InstrumentGarbageCollectionBenchmark.ThreadState benchmarkSetup =
4343
new InstrumentGarbageCollectionBenchmark.ThreadState();

sdk/metrics/src/jmhBasedTest/java/io/opentelemetry/sdk/metrics/internal/state/TestInstrumentType.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import io.opentelemetry.sdk.metrics.Aggregation;
1010
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
1111
import io.opentelemetry.sdk.metrics.internal.state.tester.AsyncCounterTester;
12+
import io.opentelemetry.sdk.metrics.internal.state.tester.ExplicitBucketHistogramTester;
1213
import io.opentelemetry.sdk.metrics.internal.state.tester.ExponentialHistogramTester;
1314
import java.util.List;
1415
import java.util.Random;
@@ -25,6 +26,12 @@ InstrumentTester createInstrumentTester() {
2526
InstrumentTester createInstrumentTester() {
2627
return new ExponentialHistogramTester();
2728
}
29+
},
30+
EXPLICIT_BUCKET() {
31+
@Override
32+
InstrumentTester createInstrumentTester() {
33+
return new ExplicitBucketHistogramTester();
34+
}
2835
};
2936

3037
abstract InstrumentTester createInstrumentTester();
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.metrics.internal.state.tester;
7+
8+
import io.opentelemetry.api.common.Attributes;
9+
import io.opentelemetry.api.metrics.DoubleHistogram;
10+
import io.opentelemetry.sdk.metrics.Aggregation;
11+
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
12+
import io.opentelemetry.sdk.metrics.internal.aggregator.ExplicitBucketHistogramUtils;
13+
import io.opentelemetry.sdk.metrics.internal.state.TestInstrumentType.InstrumentTester;
14+
import io.opentelemetry.sdk.metrics.internal.state.TestInstrumentType.TestInstrumentsState;
15+
import java.util.List;
16+
import java.util.Random;
17+
18+
public class ExplicitBucketHistogramTester implements InstrumentTester {
19+
20+
static class ExplicitHistogramState implements TestInstrumentsState {
21+
public double maxBucketValue;
22+
DoubleHistogram doubleHistogram;
23+
}
24+
25+
private static final int measurementsPerAttributeSet = 1_000;
26+
27+
@Override
28+
public Aggregation testedAggregation() {
29+
return Aggregation.explicitBucketHistogram();
30+
}
31+
32+
@Override
33+
public TestInstrumentsState buildInstruments(
34+
double instrumentCount,
35+
SdkMeterProvider sdkMeterProvider,
36+
List<Attributes> attributesList,
37+
Random random) {
38+
ExplicitHistogramState state = new ExplicitHistogramState();
39+
state.doubleHistogram =
40+
sdkMeterProvider.get("meter").histogramBuilder("test.explicit.histogram").build();
41+
state.maxBucketValue =
42+
ExplicitBucketHistogramUtils.DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES.get(
43+
ExplicitBucketHistogramUtils.DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES.size() - 1);
44+
return state;
45+
}
46+
47+
@SuppressWarnings("ForLoopReplaceableByForEach") // This is for GC sensitivity testing: no streams
48+
@Override
49+
public void recordValuesInInstruments(
50+
TestInstrumentsState testInstrumentsState, List<Attributes> attributesList, Random random) {
51+
52+
ExplicitHistogramState state = (ExplicitHistogramState) testInstrumentsState;
53+
54+
for (int j = 0; j < attributesList.size(); j++) {
55+
Attributes attributes = attributesList.get(j);
56+
for (int i = 0; i < measurementsPerAttributeSet; i++) {
57+
state.doubleHistogram.record(
58+
random.nextInt(Double.valueOf(state.maxBucketValue * 1.1).intValue()), attributes);
59+
}
60+
}
61+
}
62+
}

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleExplicitBucketHistogramAggregator.java

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.opentelemetry.api.common.Attributes;
99
import io.opentelemetry.api.internal.GuardedBy;
1010
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
11+
import io.opentelemetry.sdk.common.export.MemoryMode;
1112
import io.opentelemetry.sdk.internal.PrimitiveLongList;
1213
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
1314
import io.opentelemetry.sdk.metrics.data.DoubleExemplarData;
@@ -16,6 +17,7 @@
1617
import io.opentelemetry.sdk.metrics.internal.data.ImmutableHistogramData;
1718
import io.opentelemetry.sdk.metrics.internal.data.ImmutableHistogramPointData;
1819
import io.opentelemetry.sdk.metrics.internal.data.ImmutableMetricData;
20+
import io.opentelemetry.sdk.metrics.internal.data.MutableHistogramPointData;
1921
import io.opentelemetry.sdk.metrics.internal.descriptor.MetricDescriptor;
2022
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarReservoir;
2123
import io.opentelemetry.sdk.resources.Resource;
@@ -26,6 +28,7 @@
2628
import java.util.List;
2729
import java.util.concurrent.locks.ReentrantLock;
2830
import java.util.function.Supplier;
31+
import javax.annotation.Nullable;
2932

3033
/**
3134
* Aggregator that generates explicit bucket histograms.
@@ -36,6 +39,7 @@
3639
public final class DoubleExplicitBucketHistogramAggregator
3740
implements Aggregator<HistogramPointData, DoubleExemplarData> {
3841
private final double[] boundaries;
42+
private final MemoryMode memoryMode;
3943

4044
// a cache for converting to MetricData
4145
private final List<Double> boundaryList;
@@ -47,10 +51,14 @@ public final class DoubleExplicitBucketHistogramAggregator
4751
*
4852
* @param boundaries Bucket boundaries, in-order.
4953
* @param reservoirSupplier Supplier of exemplar reservoirs per-stream.
54+
* @param memoryMode The {@link MemoryMode} to use in this aggregator.
5055
*/
5156
public DoubleExplicitBucketHistogramAggregator(
52-
double[] boundaries, Supplier<ExemplarReservoir<DoubleExemplarData>> reservoirSupplier) {
57+
double[] boundaries,
58+
Supplier<ExemplarReservoir<DoubleExemplarData>> reservoirSupplier,
59+
MemoryMode memoryMode) {
5360
this.boundaries = boundaries;
61+
this.memoryMode = memoryMode;
5462

5563
List<Double> boundaryList = new ArrayList<>(this.boundaries.length);
5664
for (double v : this.boundaries) {
@@ -62,7 +70,7 @@ public DoubleExplicitBucketHistogramAggregator(
6270

6371
@Override
6472
public AggregatorHandle<HistogramPointData, DoubleExemplarData> createHandle() {
65-
return new Handle(this.boundaryList, this.boundaries, reservoirSupplier.get());
73+
return new Handle(this.boundaryList, this.boundaries, reservoirSupplier.get(), memoryMode);
6674
}
6775

6876
@Override
@@ -104,10 +112,14 @@ static final class Handle extends AggregatorHandle<HistogramPointData, DoubleExe
104112

105113
private final ReentrantLock lock = new ReentrantLock();
106114

115+
// Used only when MemoryMode = REUSABLE_DATA
116+
@Nullable private MutableHistogramPointData reusablePoint;
117+
107118
Handle(
108119
List<Double> boundaryList,
109120
double[] boundaries,
110-
ExemplarReservoir<DoubleExemplarData> reservoir) {
121+
ExemplarReservoir<DoubleExemplarData> reservoir,
122+
MemoryMode memoryMode) {
111123
super(reservoir);
112124
this.boundaryList = boundaryList;
113125
this.boundaries = boundaries;
@@ -116,6 +128,9 @@ static final class Handle extends AggregatorHandle<HistogramPointData, DoubleExe
116128
this.min = Double.MAX_VALUE;
117129
this.max = -1;
118130
this.count = 0;
131+
if (memoryMode == MemoryMode.REUSABLE_DATA) {
132+
this.reusablePoint = new MutableHistogramPointData(counts.length);
133+
}
119134
}
120135

121136
@Override
@@ -127,19 +142,36 @@ protected HistogramPointData doAggregateThenMaybeReset(
127142
boolean reset) {
128143
lock.lock();
129144
try {
130-
HistogramPointData pointData =
131-
ImmutableHistogramPointData.create(
132-
startEpochNanos,
133-
epochNanos,
134-
attributes,
135-
sum,
136-
this.count > 0,
137-
this.min,
138-
this.count > 0,
139-
this.max,
140-
boundaryList,
141-
PrimitiveLongList.wrap(Arrays.copyOf(counts, counts.length)),
142-
exemplars);
145+
HistogramPointData pointData;
146+
if (reusablePoint == null) {
147+
pointData =
148+
ImmutableHistogramPointData.create(
149+
startEpochNanos,
150+
epochNanos,
151+
attributes,
152+
sum,
153+
this.count > 0,
154+
this.min,
155+
this.count > 0,
156+
this.max,
157+
boundaryList,
158+
PrimitiveLongList.wrap(Arrays.copyOf(counts, counts.length)),
159+
exemplars);
160+
} else /* REUSABLE_DATA */ {
161+
pointData =
162+
reusablePoint.set(
163+
startEpochNanos,
164+
epochNanos,
165+
attributes,
166+
sum,
167+
this.count > 0,
168+
this.min,
169+
this.count > 0,
170+
this.max,
171+
boundaryList,
172+
counts,
173+
exemplars);
174+
}
143175
if (reset) {
144176
this.sum = 0;
145177
this.min = Double.MAX_VALUE;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.metrics.internal.data;
7+
8+
import io.opentelemetry.sdk.metrics.data.HistogramPointData;
9+
import java.util.List;
10+
11+
/**
12+
* Validations for {@link HistogramPointData}.
13+
*
14+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
15+
* at any time.
16+
*/
17+
final class HistogramPointDataValidations {
18+
19+
private HistogramPointDataValidations() {}
20+
21+
static void validateIsStrictlyIncreasing(List<Double> xs) {
22+
for (int i = 0; i < xs.size() - 1; i++) {
23+
if (xs.get(i).compareTo(xs.get(i + 1)) >= 0) {
24+
throw new IllegalArgumentException("invalid boundaries: " + xs);
25+
}
26+
}
27+
}
28+
29+
static void validateFiniteBoundaries(List<Double> boundaries) {
30+
if (!boundaries.isEmpty()
31+
&& (boundaries.get(0).isInfinite() || boundaries.get(boundaries.size() - 1).isInfinite())) {
32+
throw new IllegalArgumentException("invalid boundaries: contains explicit +/-Inf");
33+
}
34+
}
35+
}

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/data/ImmutableHistogramPointData.java

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
package io.opentelemetry.sdk.metrics.internal.data;
77

8+
import static io.opentelemetry.sdk.metrics.internal.data.HistogramPointDataValidations.validateFiniteBoundaries;
9+
import static io.opentelemetry.sdk.metrics.internal.data.HistogramPointDataValidations.validateIsStrictlyIncreasing;
10+
811
import com.google.auto.value.AutoValue;
912
import io.opentelemetry.api.common.Attributes;
1013
import io.opentelemetry.sdk.internal.PrimitiveLongList;
@@ -85,13 +88,8 @@ public static ImmutableHistogramPointData create(
8588
+ " instead of "
8689
+ counts.size());
8790
}
88-
if (!isStrictlyIncreasing(boundaries)) {
89-
throw new IllegalArgumentException("invalid boundaries: " + boundaries);
90-
}
91-
if (!boundaries.isEmpty()
92-
&& (boundaries.get(0).isInfinite() || boundaries.get(boundaries.size() - 1).isInfinite())) {
93-
throw new IllegalArgumentException("invalid boundaries: contains explicit +/-Inf");
94-
}
91+
validateIsStrictlyIncreasing(boundaries);
92+
validateFiniteBoundaries(boundaries);
9593

9694
long totalCount = 0;
9795
for (long c : PrimitiveLongList.toArray(counts)) {
@@ -113,13 +111,4 @@ public static ImmutableHistogramPointData create(
113111
}
114112

115113
ImmutableHistogramPointData() {}
116-
117-
private static boolean isStrictlyIncreasing(List<Double> xs) {
118-
for (int i = 0; i < xs.size() - 1; i++) {
119-
if (xs.get(i).compareTo(xs.get(i + 1)) >= 0) {
120-
return false;
121-
}
122-
}
123-
return true;
124-
}
125114
}

0 commit comments

Comments
 (0)