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:
parent
85d705d841
commit
966c358ef1
|
|
@ -179,6 +179,18 @@ public abstract class Serializer implements AutoCloseable {
|
||||||
writeEndRepeatedPrimitive();
|
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. */
|
/** Serializes a {@code repeated double} field. */
|
||||||
public void serializeRepeatedDouble(ProtoFieldInfo field, List<Double> values)
|
public void serializeRepeatedDouble(ProtoFieldInfo field, List<Double> values)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import io.opentelemetry.exporter.otlp.internal.MarshalerUtil;
|
||||||
import io.opentelemetry.exporter.otlp.internal.MarshalerWithSize;
|
import io.opentelemetry.exporter.otlp.internal.MarshalerWithSize;
|
||||||
import io.opentelemetry.exporter.otlp.internal.Serializer;
|
import io.opentelemetry.exporter.otlp.internal.Serializer;
|
||||||
import io.opentelemetry.proto.metrics.v1.internal.HistogramDataPoint;
|
import io.opentelemetry.proto.metrics.v1.internal.HistogramDataPoint;
|
||||||
|
import io.opentelemetry.sdk.internal.PrimitiveLongList;
|
||||||
import io.opentelemetry.sdk.metrics.data.DoubleHistogramPointData;
|
import io.opentelemetry.sdk.metrics.data.DoubleHistogramPointData;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
@ -85,7 +86,8 @@ final class HistogramDataPointMarshaler extends MarshalerWithSize {
|
||||||
output.serializeFixed64(HistogramDataPoint.TIME_UNIX_NANO, timeUnixNano);
|
output.serializeFixed64(HistogramDataPoint.TIME_UNIX_NANO, timeUnixNano);
|
||||||
output.serializeFixed64(HistogramDataPoint.COUNT, count);
|
output.serializeFixed64(HistogramDataPoint.COUNT, count);
|
||||||
output.serializeDouble(HistogramDataPoint.SUM, sum);
|
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.serializeRepeatedDouble(HistogramDataPoint.EXPLICIT_BOUNDS, explicitBounds);
|
||||||
output.serializeRepeatedMessage(HistogramDataPoint.EXEMPLARS, exemplars);
|
output.serializeRepeatedMessage(HistogramDataPoint.EXEMPLARS, exemplars);
|
||||||
output.serializeRepeatedMessage(HistogramDataPoint.ATTRIBUTES, attributes);
|
output.serializeRepeatedMessage(HistogramDataPoint.ATTRIBUTES, attributes);
|
||||||
|
|
|
||||||
|
|
@ -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() {}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ package io.opentelemetry.sdk.metrics.data;
|
||||||
|
|
||||||
import com.google.auto.value.AutoValue;
|
import com.google.auto.value.AutoValue;
|
||||||
import io.opentelemetry.api.common.Attributes;
|
import io.opentelemetry.api.common.Attributes;
|
||||||
|
import io.opentelemetry.sdk.internal.PrimitiveLongList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -69,7 +70,7 @@ public abstract class DoubleHistogramPointData implements PointData {
|
||||||
}
|
}
|
||||||
|
|
||||||
long totalCount = 0;
|
long totalCount = 0;
|
||||||
for (long c : counts) {
|
for (long c : PrimitiveLongList.toArray(counts)) {
|
||||||
totalCount += c;
|
totalCount += c;
|
||||||
}
|
}
|
||||||
return new AutoValue_DoubleHistogramPointData(
|
return new AutoValue_DoubleHistogramPointData(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
package io.opentelemetry.sdk.metrics.internal.aggregator;
|
package io.opentelemetry.sdk.metrics.internal.aggregator;
|
||||||
|
|
||||||
import io.opentelemetry.api.common.Attributes;
|
import io.opentelemetry.api.common.Attributes;
|
||||||
|
import io.opentelemetry.sdk.internal.PrimitiveLongList;
|
||||||
import io.opentelemetry.sdk.metrics.common.InstrumentType;
|
import io.opentelemetry.sdk.metrics.common.InstrumentType;
|
||||||
import io.opentelemetry.sdk.metrics.data.DoubleHistogramPointData;
|
import io.opentelemetry.sdk.metrics.data.DoubleHistogramPointData;
|
||||||
import io.opentelemetry.sdk.metrics.data.DoublePointData;
|
import io.opentelemetry.sdk.metrics.data.DoublePointData;
|
||||||
|
|
@ -77,10 +78,7 @@ final class MetricDataUtils {
|
||||||
List<DoubleHistogramPointData> points = new ArrayList<>(accumulationMap.size());
|
List<DoubleHistogramPointData> points = new ArrayList<>(accumulationMap.size());
|
||||||
accumulationMap.forEach(
|
accumulationMap.forEach(
|
||||||
(labels, aggregator) -> {
|
(labels, aggregator) -> {
|
||||||
List<Long> counts = new ArrayList<>(aggregator.getCounts().length);
|
List<Long> counts = PrimitiveLongList.wrap(aggregator.getCounts().clone());
|
||||||
for (long v : aggregator.getCounts()) {
|
|
||||||
counts.add(v);
|
|
||||||
}
|
|
||||||
points.add(
|
points.add(
|
||||||
DoubleHistogramPointData.create(
|
DoubleHistogramPointData.create(
|
||||||
startEpochNanos,
|
startEpochNanos,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue