Use a wrapper of primitive long array for histogram counts. (#3854)

* Use a wrapper of primitive long array for histogram counts.

* Fix

* Better
This commit is contained in:
Anuraag Agrawal 2021-11-16 10:53:09 +09:00 committed by GitHub
parent 85d705d841
commit 966c358ef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 127 additions and 6 deletions

View File

@ -179,6 +179,18 @@ public abstract class Serializer implements AutoCloseable {
writeEndRepeatedPrimitive();
}
/** Serializes a {@code repeated fixed64} field. */
public void serializeRepeatedFixed64(ProtoFieldInfo field, long[] values) throws IOException {
if (values.length == 0) {
return;
}
writeStartRepeatedPrimitive(field, WireFormat.FIXED64_SIZE, values.length);
for (long value : values) {
writeFixed64Value(value);
}
writeEndRepeatedPrimitive();
}
/** Serializes a {@code repeated double} field. */
public void serializeRepeatedDouble(ProtoFieldInfo field, List<Double> values)
throws IOException {

View File

@ -10,6 +10,7 @@ import io.opentelemetry.exporter.otlp.internal.MarshalerUtil;
import io.opentelemetry.exporter.otlp.internal.MarshalerWithSize;
import io.opentelemetry.exporter.otlp.internal.Serializer;
import io.opentelemetry.proto.metrics.v1.internal.HistogramDataPoint;
import io.opentelemetry.sdk.internal.PrimitiveLongList;
import io.opentelemetry.sdk.metrics.data.DoubleHistogramPointData;
import java.io.IOException;
import java.util.Collection;
@ -85,7 +86,8 @@ final class HistogramDataPointMarshaler extends MarshalerWithSize {
output.serializeFixed64(HistogramDataPoint.TIME_UNIX_NANO, timeUnixNano);
output.serializeFixed64(HistogramDataPoint.COUNT, count);
output.serializeDouble(HistogramDataPoint.SUM, sum);
output.serializeRepeatedFixed64(HistogramDataPoint.BUCKET_COUNTS, bucketCounts);
output.serializeRepeatedFixed64(
HistogramDataPoint.BUCKET_COUNTS, PrimitiveLongList.toArray(bucketCounts));
output.serializeRepeatedDouble(HistogramDataPoint.EXPLICIT_BOUNDS, explicitBounds);
output.serializeRepeatedMessage(HistogramDataPoint.EXEMPLARS, exemplars);
output.serializeRepeatedMessage(HistogramDataPoint.ATTRIBUTES, attributes);

View File

@ -0,0 +1,68 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.internal;
import java.util.AbstractList;
import java.util.List;
/**
* A list of longs backed by, and exposing, an array of primitives. Values will be boxed on demand
* when using standard List operations. Operations should generally use the static methods in this
* class to operate directly on the backing array instead. The idea is that in almost all apps, the
* list will only be accessed by our internal code, and if it does happen to be used elsewhere,
* performance of on-demand boxing isn't prohibitive while still providing expected ergonomics.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class PrimitiveLongList {
/**
* Returns a list that wraps the primitive array. Modifications in the array will be visible in
* the list.
*/
public static List<Long> wrap(long[] values) {
return new LongListImpl(values);
}
/**
* Returns a primitive array with the values of the list. The list should generally have been
* created with {@link PrimitiveLongList#wrap(long[])}.
*/
public static long[] toArray(List<Long> list) {
if (list instanceof LongListImpl) {
return ((LongListImpl) list).values;
}
long[] values = new long[list.size()];
for (int i = 0; i < values.length; i++) {
values[i] = list.get(i);
}
return values;
}
private static class LongListImpl extends AbstractList<Long> {
private final long[] values;
LongListImpl(long[] values) {
this.values = values;
}
@Override
public Long get(int index) {
// If out of bounds, the array access will produce a perfectly fine IndexOutOfBoundsException.
return values[index];
}
@Override
public int size() {
return values.length;
}
}
private PrimitiveLongList() {}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.internal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.catchThrowable;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
class PrimitiveLongListTest {
@Test
void wrap() {
long[] array = new long[] {1, 2};
List<Long> wrapped = PrimitiveLongList.wrap(array);
// Standard List operations
assertThat(wrapped).containsExactly(1L, 2L);
assertThat(wrapped).hasSize(2);
// Message can change between Java versions, so instead check it's the same as a normal List's
// exception.
Throwable referenceException = catchThrowable(() -> Arrays.asList(1, 2).get(3));
assertThatThrownBy(() -> wrapped.get(3))
.isInstanceOf(IndexOutOfBoundsException.class)
.hasMessage(referenceException.getMessage());
assertThat(PrimitiveLongList.toArray(wrapped)).isSameAs(array).containsExactly(1L, 2L);
}
@Test
void notWrapped() {
List<Long> list = Arrays.asList(1L, 2L);
assertThat(PrimitiveLongList.toArray(list)).containsExactly(1L, 2L);
}
}

View File

@ -7,6 +7,7 @@ package io.opentelemetry.sdk.metrics.data;
import com.google.auto.value.AutoValue;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.internal.PrimitiveLongList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -69,7 +70,7 @@ public abstract class DoubleHistogramPointData implements PointData {
}
long totalCount = 0;
for (long c : counts) {
for (long c : PrimitiveLongList.toArray(counts)) {
totalCount += c;
}
return new AutoValue_DoubleHistogramPointData(

View File

@ -6,6 +6,7 @@
package io.opentelemetry.sdk.metrics.internal.aggregator;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.internal.PrimitiveLongList;
import io.opentelemetry.sdk.metrics.common.InstrumentType;
import io.opentelemetry.sdk.metrics.data.DoubleHistogramPointData;
import io.opentelemetry.sdk.metrics.data.DoublePointData;
@ -77,10 +78,7 @@ final class MetricDataUtils {
List<DoubleHistogramPointData> points = new ArrayList<>(accumulationMap.size());
accumulationMap.forEach(
(labels, aggregator) -> {
List<Long> counts = new ArrayList<>(aggregator.getCounts().length);
for (long v : aggregator.getCounts()) {
counts.add(v);
}
List<Long> counts = PrimitiveLongList.wrap(aggregator.getCounts().clone());
points.add(
DoubleHistogramPointData.create(
startEpochNanos,