-
Notifications
You must be signed in to change notification settings - Fork 993
Histogram Aggregation - core SDK - aggregator #2924
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jkwatson
merged 16 commits into
open-telemetry:main
from
atlassian-forks:histogram-aggregation-core-aggregator
Mar 1, 2021
Merged
Changes from 4 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
e675fd1
add histogram aggregator
beanliu 7ffbc2a
implement DoubleHistogramAggregator.toMetricData
beanliu a0aff16
pass temporality instead of a boolean for the creation of histogram a…
beanliu 9456910
a53b6c9
remove ImmutableDoubleArray
beanliu 9062d33
remove ImmutableLongArray
beanliu 590f0df
fixup! remove ImmutableDoubleArray
beanliu bc80f02
simplify the implementation of DoubleHistogramAggregator
beanliu 826523b
accumulate value with configured boundaries
beanliu 8af20d1
use nanoseconds as timeunit
beanliu 9c41d8d
update benchmark mode
beanliu 6e2dfde
List<Double> instead of double[] for histogram factory
beanliu cb8bf6b
update var names
beanliu dc92511
switch to using assertj
beanliu 4d75f5a
simpler boundary check
beanliu cd43a0e
simplify multi-threaded test
beanliu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
75 changes: 75 additions & 0 deletions
75
...etrics/src/jmh/java/io/opentelemetry/sdk/metrics/aggregator/DoubleHistogramBenchmark.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.sdk.metrics.aggregator; | ||
|
|
||
| import io.opentelemetry.sdk.common.InstrumentationLibraryInfo; | ||
| import io.opentelemetry.sdk.metrics.common.InstrumentDescriptor; | ||
| import io.opentelemetry.sdk.metrics.common.InstrumentType; | ||
| import io.opentelemetry.sdk.metrics.common.InstrumentValueType; | ||
| import io.opentelemetry.sdk.metrics.data.AggregationTemporality; | ||
| import io.opentelemetry.sdk.resources.Resource; | ||
| import java.util.concurrent.TimeUnit; | ||
| import org.openjdk.jmh.annotations.Benchmark; | ||
| import org.openjdk.jmh.annotations.Fork; | ||
| import org.openjdk.jmh.annotations.Level; | ||
| import org.openjdk.jmh.annotations.Measurement; | ||
| import org.openjdk.jmh.annotations.OutputTimeUnit; | ||
| import org.openjdk.jmh.annotations.Scope; | ||
| import org.openjdk.jmh.annotations.Setup; | ||
| import org.openjdk.jmh.annotations.State; | ||
| import org.openjdk.jmh.annotations.Threads; | ||
| import org.openjdk.jmh.annotations.Warmup; | ||
|
|
||
| @State(Scope.Benchmark) | ||
| public class DoubleHistogramBenchmark { | ||
| private static final Aggregator<HistogramAccumulation> aggregator = | ||
| AggregatorFactory.histogram(new double[] {10, 100, 1_000}, AggregationTemporality.DELTA) | ||
| .create( | ||
| Resource.getDefault(), | ||
| InstrumentationLibraryInfo.empty(), | ||
| InstrumentDescriptor.create( | ||
| "name", | ||
| "description", | ||
| "1", | ||
| InstrumentType.VALUE_RECORDER, | ||
| InstrumentValueType.DOUBLE)); | ||
| private AggregatorHandle<HistogramAccumulation> aggregatorHandle; | ||
|
|
||
| @Setup(Level.Trial) | ||
| public final void setup() { | ||
| aggregatorHandle = aggregator.createHandle(); | ||
| } | ||
|
|
||
| @Benchmark | ||
| @Fork(1) | ||
| @Warmup(iterations = 5, time = 1) | ||
| @Measurement(iterations = 10, time = 1) | ||
| @OutputTimeUnit(TimeUnit.MILLISECONDS) | ||
| @Threads(value = 10) | ||
| public void aggregate_10Threads() { | ||
|
bogdandrutu marked this conversation as resolved.
|
||
| aggregatorHandle.recordDouble(100.0056); | ||
| } | ||
|
|
||
| @Benchmark | ||
| @Fork(1) | ||
| @Warmup(iterations = 5, time = 1) | ||
| @Measurement(iterations = 10, time = 1) | ||
| @OutputTimeUnit(TimeUnit.MILLISECONDS) | ||
| @Threads(value = 5) | ||
| public void aggregate_5Threads() { | ||
| aggregatorHandle.recordDouble(100.0056); | ||
| } | ||
|
|
||
| @Benchmark | ||
| @Fork(1) | ||
| @Warmup(iterations = 5, time = 1) | ||
| @Measurement(iterations = 10, time = 1) | ||
| @OutputTimeUnit(TimeUnit.MILLISECONDS) | ||
| @Threads(value = 1) | ||
| public void aggregate_1Threads() { | ||
| aggregatorHandle.recordDouble(100.0056); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
158 changes: 158 additions & 0 deletions
158
...rics/src/main/java/io/opentelemetry/sdk/metrics/aggregator/DoubleHistogramAggregator.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.sdk.metrics.aggregator; | ||
|
|
||
| import io.opentelemetry.api.metrics.common.Labels; | ||
| import io.opentelemetry.sdk.common.InstrumentationLibraryInfo; | ||
| import io.opentelemetry.sdk.metrics.common.InstrumentDescriptor; | ||
| import io.opentelemetry.sdk.metrics.data.AggregationTemporality; | ||
| import io.opentelemetry.sdk.metrics.data.DoubleHistogramData; | ||
| import io.opentelemetry.sdk.metrics.data.MetricData; | ||
| import io.opentelemetry.sdk.resources.Resource; | ||
| import java.util.Arrays; | ||
| import java.util.Map; | ||
| import java.util.concurrent.locks.ReentrantLock; | ||
| import javax.annotation.concurrent.GuardedBy; | ||
|
|
||
| final class DoubleHistogramAggregator extends AbstractAggregator<HistogramAccumulation> { | ||
| private final ImmutableDoubleArray boundaries; | ||
|
|
||
| DoubleHistogramAggregator( | ||
| Resource resource, | ||
| InstrumentationLibraryInfo instrumentationLibraryInfo, | ||
| InstrumentDescriptor instrumentDescriptor, | ||
| ImmutableDoubleArray boundaries, | ||
| boolean stateful) { | ||
| super(resource, instrumentationLibraryInfo, instrumentDescriptor, stateful); | ||
| this.boundaries = boundaries; | ||
| } | ||
|
|
||
| @Override | ||
| public AggregatorHandle<HistogramAccumulation> createHandle() { | ||
| return new Handle(this.boundaries); | ||
| } | ||
|
|
||
| /** | ||
| * Return the result of the merge of two histogram accumulations. As long as one Aggregator | ||
|
beanliu marked this conversation as resolved.
|
||
| * instance produces all Accumulations with constant boundaries we don't need to worry about | ||
| * merging accumulations with different boundaries. | ||
| */ | ||
| @Override | ||
| public final HistogramAccumulation merge(HistogramAccumulation x, HistogramAccumulation y) { | ||
| long[] mergedCounts = new long[x.getCounts().length()]; | ||
| for (int i = 0; i < x.getCounts().length(); ++i) { | ||
| mergedCounts[i] = x.getCounts().get(i) + y.getCounts().get(i); | ||
| } | ||
| return HistogramAccumulation.create( | ||
| x.getSum() + y.getSum(), ImmutableLongArray.copyOf(mergedCounts)); | ||
|
beanliu marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| @Override | ||
| public final MetricData toMetricData( | ||
|
beanliu marked this conversation as resolved.
|
||
| Map<Labels, HistogramAccumulation> accumulationByLabels, | ||
| long startEpochNanos, | ||
| long lastCollectionEpoch, | ||
| long epochNanos) { | ||
| return MetricData.createDoubleHistogram( | ||
| getResource(), | ||
| getInstrumentationLibraryInfo(), | ||
| getInstrumentDescriptor().getName(), | ||
| getInstrumentDescriptor().getDescription(), | ||
| getInstrumentDescriptor().getUnit(), | ||
| DoubleHistogramData.create( | ||
| isStateful() ? AggregationTemporality.CUMULATIVE : AggregationTemporality.DELTA, | ||
| MetricDataUtils.toDoubleHistogramPointList( | ||
| accumulationByLabels, | ||
| isStateful() ? startEpochNanos : lastCollectionEpoch, | ||
| epochNanos, | ||
| boundaries.toList()))); | ||
| } | ||
|
|
||
| @Override | ||
| public HistogramAccumulation accumulateDouble(double value) { | ||
| return HistogramAccumulation.create(value, ImmutableLongArray.of(1)); | ||
| } | ||
|
|
||
| @Override | ||
| public HistogramAccumulation accumulateLong(long value) { | ||
| return HistogramAccumulation.create(value, ImmutableLongArray.of(1)); | ||
| } | ||
|
|
||
| static final class Handle extends AggregatorHandle<HistogramAccumulation> { | ||
| private final ImmutableDoubleArray boundaries; | ||
|
beanliu marked this conversation as resolved.
Outdated
|
||
|
|
||
| private final ReentrantLock lock = new ReentrantLock(); | ||
|
|
||
| @GuardedBy("lock") | ||
| private final State current; | ||
|
|
||
| Handle(ImmutableDoubleArray boundaries) { | ||
| this.boundaries = boundaries; | ||
| this.current = new State(this.boundaries.length() + 1); | ||
| } | ||
|
|
||
| // Benchmark shows that linear search performs better than binary search with ordinary | ||
| // buckets. | ||
| private int findBucketIndex(double value) { | ||
| for (int i = 0; i < boundaries.length(); ++i) { | ||
| if (Double.compare(value, boundaries.get(i)) <= 0) { | ||
|
beanliu marked this conversation as resolved.
Outdated
|
||
| return i; | ||
| } | ||
| } | ||
| return boundaries.length(); | ||
| } | ||
|
|
||
| @Override | ||
| protected HistogramAccumulation doAccumulateThenReset() { | ||
| lock.lock(); | ||
| try { | ||
| HistogramAccumulation acc = | ||
| HistogramAccumulation.create(current.sum, ImmutableLongArray.copyOf(current.counts)); | ||
| current.reset(); | ||
| return acc; | ||
| } finally { | ||
| lock.unlock(); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| protected void doRecordDouble(double value) { | ||
| int bucketIndex = findBucketIndex(value); | ||
|
|
||
| lock.lock(); | ||
| try { | ||
| current.record(bucketIndex, value); | ||
| } finally { | ||
| lock.unlock(); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| protected void doRecordLong(long value) { | ||
| doRecordDouble((double) value); | ||
| } | ||
|
|
||
| private static final class State { | ||
|
beanliu marked this conversation as resolved.
Outdated
|
||
| private double sum; | ||
| private final long[] counts; | ||
|
|
||
| public State(int bucketSize) { | ||
| this.counts = new long[bucketSize]; | ||
| reset(); | ||
| } | ||
|
|
||
| private void reset() { | ||
| this.sum = 0; | ||
| Arrays.fill(this.counts, 0); | ||
| } | ||
|
|
||
| private void record(int bucketIndex, double value) { | ||
| this.sum += value; | ||
| this.counts[bucketIndex]++; | ||
| } | ||
| } | ||
| } | ||
| } | ||
38 changes: 38 additions & 0 deletions
38
sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/aggregator/HistogramAccumulation.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.sdk.metrics.aggregator; | ||
|
|
||
| import com.google.auto.value.AutoValue; | ||
| import javax.annotation.concurrent.Immutable; | ||
|
|
||
| @Immutable | ||
| @AutoValue | ||
| abstract class HistogramAccumulation { | ||
| /** | ||
| * Creates a new {@link HistogramAccumulation} with the given values. | ||
| * | ||
| * @return a new {@link HistogramAccumulation} with the given values. | ||
| */ | ||
| static HistogramAccumulation create(double sum, ImmutableLongArray counts) { | ||
| return new AutoValue_HistogramAccumulation(sum, counts); | ||
| } | ||
|
|
||
| HistogramAccumulation() {} | ||
|
|
||
| /** | ||
| * The sum of all measurements recorded. | ||
| * | ||
| * @return the sum of recorded measurements. | ||
| */ | ||
| abstract double getSum(); | ||
|
|
||
| /** | ||
| * The counts in each bucket. | ||
| * | ||
| * @return the counts in each bucket. | ||
| */ | ||
| abstract ImmutableLongArray getCounts(); | ||
| } |
56 changes: 56 additions & 0 deletions
56
...ics/src/main/java/io/opentelemetry/sdk/metrics/aggregator/HistogramAggregatorFactory.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.sdk.metrics.aggregator; | ||
|
|
||
| import io.opentelemetry.sdk.common.InstrumentationLibraryInfo; | ||
| import io.opentelemetry.sdk.metrics.common.InstrumentDescriptor; | ||
| import io.opentelemetry.sdk.metrics.data.AggregationTemporality; | ||
| import io.opentelemetry.sdk.resources.Resource; | ||
|
|
||
| final class HistogramAggregatorFactory implements AggregatorFactory { | ||
| private final ImmutableDoubleArray boundaries; | ||
| private final AggregationTemporality temporality; | ||
|
|
||
| HistogramAggregatorFactory(double[] boundaries, AggregationTemporality temporality) { | ||
| this.boundaries = ImmutableDoubleArray.copyOf(boundaries); | ||
| this.temporality = temporality; | ||
|
|
||
|
beanliu marked this conversation as resolved.
|
||
| for (int i = 1; i < this.boundaries.length(); ++i) { | ||
| if (Double.compare(this.boundaries.get(i - 1), this.boundaries.get(i)) >= 0) { | ||
| throw new IllegalArgumentException( | ||
| "invalid bucket boundary: " | ||
| + this.boundaries.get(i - 1) | ||
| + " >= " | ||
| + this.boundaries.get(i)); | ||
| } | ||
| } | ||
| if (this.boundaries.length() > 0) { | ||
| if (this.boundaries.get(0) == Double.NEGATIVE_INFINITY) { | ||
| throw new IllegalArgumentException("invalid bucket boundary: -Inf"); | ||
| } | ||
| if (this.boundaries.get(this.boundaries.length() - 1) == Double.POSITIVE_INFINITY) { | ||
| throw new IllegalArgumentException("invalid bucket boundary: +Inf"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| @SuppressWarnings("unchecked") | ||
| public <T> Aggregator<T> create( | ||
| Resource resource, | ||
| InstrumentationLibraryInfo instrumentationLibraryInfo, | ||
| InstrumentDescriptor descriptor) { | ||
| final boolean stateful = this.temporality == AggregationTemporality.CUMULATIVE; | ||
| switch (descriptor.getValueType()) { | ||
| case LONG: | ||
| case DOUBLE: | ||
| return (Aggregator<T>) | ||
| new DoubleHistogramAggregator( | ||
| resource, instrumentationLibraryInfo, descriptor, this.boundaries, stateful); | ||
| } | ||
| throw new IllegalArgumentException("Invalid instrument value type"); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.