diff --git a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/ExponentialHistogramIndexerBenchmark.java b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/ExponentialHistogramIndexerBenchmark.java new file mode 100644 index 00000000000..18d8bac83f6 --- /dev/null +++ b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/ExponentialHistogramIndexerBenchmark.java @@ -0,0 +1,64 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics.internal.aggregator; + +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +/** Measures runtime cost of computing bucket indexes for exponential histograms. */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Measurement(iterations = 5, time = 1) +@Warmup(iterations = 5, time = 1) +@Fork(1) +public class ExponentialHistogramIndexerBenchmark { + + @State(Scope.Thread) + public static class ThreadState { + @Param(value = {"1", "0", "-1"}) + int scale; + + private double[] values; + private final AtomicLong valueIndex = new AtomicLong(); + private ExponentialHistogramIndexer indexer; + + @Setup(Level.Trial) + public final void setup() { + Random random = new Random(); + int numValues = 2000; + values = new double[numValues]; + for (int i = 0; i < numValues; i++) { + values[i] = random.nextDouble() * 1000; + } + indexer = ExponentialHistogramIndexer.get(scale); + } + + public void compute() { + // Compute a number of samples + for (int i = 0; i < 2000; i++) { + indexer.computeIndex(values[(int) (valueIndex.incrementAndGet() % values.length)]); + } + } + } + + @Benchmark + public void computeIndex(ThreadState threadState) { + threadState.compute(); + } +} diff --git a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java index 360a0e6e407..0deae71bd80 100644 --- a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java +++ b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java @@ -25,16 +25,16 @@ public enum HistogramAggregationParam { new DoubleExponentialHistogramAggregator( ExemplarReservoir::doubleNoSamples, ExponentialBucketStrategy.newStrategy( - 20, ExponentialCounterFactory.circularBufferCounter()))), + 20, ExponentialCounterFactory.circularBufferCounter(), 0))), EXPONENTIAL_CIRCULAR_BUFFER( new DoubleExponentialHistogramAggregator( ExemplarReservoir::doubleNoSamples, ExponentialBucketStrategy.newStrategy( - 160, ExponentialCounterFactory.circularBufferCounter()))), + 160, ExponentialCounterFactory.circularBufferCounter(), 0))), EXPONENTIAL_MAP_COUNTER( new DoubleExponentialHistogramAggregator( ExemplarReservoir::doubleNoSamples, - ExponentialBucketStrategy.newStrategy(160, ExponentialCounterFactory.mapCounter()))); + ExponentialBucketStrategy.newStrategy(160, ExponentialCounterFactory.mapCounter(), 0))); private final Aggregator aggregator; diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/ExponentialBucketStrategy.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/ExponentialBucketStrategy.java index e4a1c8c6762..8a74396107d 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/ExponentialBucketStrategy.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/ExponentialBucketStrategy.java @@ -10,26 +10,35 @@ /** The configuration for how to create exponential histogram buckets. */ final class ExponentialBucketStrategy { - private static final int STARTING_SCALE = 20; + private static final int DEFAULT_STARTING_SCALE = 20; + private final int startingScale; /** The maximum number of buckets that will be used for positive or negative recordings. */ private final int maxBuckets; /** The mechanism of constructing and copying buckets. */ private final ExponentialCounterFactory counterFactory; - private ExponentialBucketStrategy(int maxBuckets, ExponentialCounterFactory counterFactory) { + private ExponentialBucketStrategy( + int startingScale, int maxBuckets, ExponentialCounterFactory counterFactory) { + this.startingScale = startingScale; this.maxBuckets = maxBuckets; this.counterFactory = counterFactory; } /** Constructs fresh new buckets with default settings. */ DoubleExponentialHistogramBuckets newBuckets() { - return new DoubleExponentialHistogramBuckets(STARTING_SCALE, maxBuckets, counterFactory); + return new DoubleExponentialHistogramBuckets(startingScale, maxBuckets, counterFactory); } /** Create a new strategy for generating Exponential Buckets. */ static ExponentialBucketStrategy newStrategy( int maxBuckets, ExponentialCounterFactory counterFactory) { - return new ExponentialBucketStrategy(maxBuckets, counterFactory); + return new ExponentialBucketStrategy(DEFAULT_STARTING_SCALE, maxBuckets, counterFactory); + } + + /** Create a new strategy for generating Exponential Buckets. */ + static ExponentialBucketStrategy newStrategy( + int maxBuckets, ExponentialCounterFactory counterFactory, int startingScale) { + return new ExponentialBucketStrategy(startingScale, maxBuckets, counterFactory); } }