From 4430a3233043c5755cbc0b4168475ede5979d7c5 Mon Sep 17 00:00:00 2001
From: jack-berg <34418638+jack-berg@users.noreply.github.com>
Date: Thu, 1 Dec 2022 09:20:19 -0600
Subject: [PATCH] Add ExponentialHistogramIndexerBenchmark (#4989)

---
 .../ExponentialHistogramIndexerBenchmark.java | 64 +++++++++++++++++++
 .../aggregator/HistogramAggregationParam.java |  6 +-
 .../aggregator/ExponentialBucketStrategy.java | 17 +++--
 3 files changed, 80 insertions(+), 7 deletions(-)
 create mode 100644 sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/ExponentialHistogramIndexerBenchmark.java

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);
   }
 }