diff --git a/exporters/common/build.gradle.kts b/exporters/common/build.gradle.kts index b8868565287..f311510f83d 100644 --- a/exporters/common/build.gradle.kts +++ b/exporters/common/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { api(project(":sdk-extensions:autoconfigure-spi")) compileOnly(project(":sdk:common")) + compileOnly(project(":exporters:common:compile-stub")) compileOnly("org.codehaus.mojo:animal-sniffer-annotations") @@ -22,6 +23,8 @@ dependencies { // We include helpers shared by gRPC exporters but do not want to impose these // dependency on all of our consumers. compileOnly("com.fasterxml.jackson.core:jackson-core") + // sun.misc.Unsafe from the JDK isn't found by the compiler, we provide our own trimmed down + // version that we can compile against. compileOnly("io.grpc:grpc-stub") testImplementation(project(":sdk:common")) @@ -31,6 +34,7 @@ dependencies { testImplementation("org.skyscreamer:jsonassert") testImplementation("com.google.api.grpc:proto-google-common-protos") testImplementation("io.grpc:grpc-testing") + testImplementation("edu.berkeley.cs.jqf:jqf-fuzz") testRuntimeOnly("io.grpc:grpc-netty-shaded") } diff --git a/exporters/common/compile-stub/build.gradle.kts b/exporters/common/compile-stub/build.gradle.kts new file mode 100644 index 00000000000..f93bd1883c9 --- /dev/null +++ b/exporters/common/compile-stub/build.gradle.kts @@ -0,0 +1,6 @@ +plugins { + id("otel.java-conventions") +} + +description = "OpenTelemetry Exporter Compile Stub" +otelJava.moduleName.set("io.opentelemetry.exporter.internal.compile-stub") diff --git a/exporters/common/compile-stub/src/main/java/sun/misc/Unsafe.java b/exporters/common/compile-stub/src/main/java/sun/misc/Unsafe.java new file mode 100644 index 00000000000..48b37ad371f --- /dev/null +++ b/exporters/common/compile-stub/src/main/java/sun/misc/Unsafe.java @@ -0,0 +1,35 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package sun.misc; + +import java.lang.reflect.Field; + +/** + * sun.misc.Unsafe from the JDK isn't found by the compiler, we provide our own trimmed down version + * that we can compile against. + */ +public class Unsafe { + + public long objectFieldOffset(Field f) { + return -1; + } + + public Object getObject(Object o, long offset) { + return null; + } + + public byte getByte(Object o, long offset) { + return 0; + } + + public int arrayBaseOffset(Class arrayClass) { + return 0; + } + + public long getLong(Object o, long offset) { + return 0; + } +} diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/MarshalerContext.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/MarshalerContext.java index fab949aeb71..a9f69459e47 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/MarshalerContext.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/MarshalerContext.java @@ -24,6 +24,7 @@ */ public final class MarshalerContext { private final boolean marshalStringNoAllocation; + private final boolean marshalStringUnsafe; private int[] sizes = new int[16]; private int sizeReadIndex; @@ -32,19 +33,23 @@ public final class MarshalerContext { private int dataReadIndex; private int dataWriteIndex; - @SuppressWarnings("BooleanParameter") public MarshalerContext() { - this(true); + this(/* marshalStringNoAllocation= */ true, /* marshalStringUnsafe= */ true); } - public MarshalerContext(boolean marshalStringNoAllocation) { + public MarshalerContext(boolean marshalStringNoAllocation, boolean marshalStringUnsafe) { this.marshalStringNoAllocation = marshalStringNoAllocation; + this.marshalStringUnsafe = marshalStringUnsafe; } public boolean marshalStringNoAllocation() { return marshalStringNoAllocation; } + public boolean marshalStringUnsafe() { + return marshalStringUnsafe; + } + public void addSize(int size) { growSizeIfNeeded(); sizes[sizeWriteIndex++] = size; diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtil.java index 87c8b3d96bb..793f90cc9ae 100644 --- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtil.java +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtil.java @@ -299,16 +299,63 @@ public static int sizeMessageWithContext( } /** Returns the size of utf8 encoded string in bytes. */ - @SuppressWarnings("UnusedVariable") private static int getUtf8Size(String string, MarshalerContext context) { - return getUtf8Size(string); + return getUtf8Size(string, context.marshalStringUnsafe()); } // Visible for testing - static int getUtf8Size(String string) { + static int getUtf8Size(String string, boolean useUnsafe) { + if (useUnsafe && UnsafeString.isAvailable() && UnsafeString.isLatin1(string)) { + byte[] bytes = UnsafeString.getBytes(string); + // latin1 bytes with negative value (most significant bit set) are encoded as 2 bytes in utf8 + return string.length() + countNegative(bytes); + } + return encodedUtf8Length(string); } + // Inner loop can process at most 8 * 255 bytes without overflowing counter. To process more bytes + // inner loop has to be run multiple times. + private static final int MAX_INNER_LOOP_SIZE = 8 * 255; + // mask that selects only the most significant bit in every byte of the long + private static final long MOST_SIGNIFICANT_BIT_MASK = 0x8080808080808080L; + + /** Returns the count of bytes with negative value. */ + private static int countNegative(byte[] bytes) { + int count = 0; + int offset = 0; + // We are processing one long (8 bytes) at a time. In the inner loop we are keeping counts in a + // long where each byte in the long is a separate counter. Due to this the inner loop can + // process a maximum of 8*255 bytes at a time without overflow. + for (int i = 1; i <= bytes.length / MAX_INNER_LOOP_SIZE + 1; i++) { + long tmp = 0; // each byte in this long is a separate counter + int limit = Math.min(i * MAX_INNER_LOOP_SIZE, bytes.length & ~7); + for (; offset < limit; offset += 8) { + long value = UnsafeString.getLong(bytes, offset); + // Mask the value keeping only the most significant bit in each byte and then shift this bit + // to the position of the least significant bit in each byte. If the input byte was not + // negative then after this transformation it will be zero, if it was negative then it will + // be one. + tmp += (value & MOST_SIGNIFICANT_BIT_MASK) >>> 7; + } + // sum up counts + if (tmp != 0) { + for (int j = 0; j < 8; j++) { + count += (int) (tmp & 0xff); + tmp = tmp >>> 8; + } + } + } + + // Handle remaining bytes. Previous loop processes 8 bytes a time, if the input size is not + // divisible with 8 the remaining bytes are handled here. + for (int i = offset; i < bytes.length; i++) { + // same as if (bytes[i] < 0) count++; + count += bytes[i] >>> 31; + } + return count; + } + // adapted from // https://github.com/protocolbuffers/protobuf/blob/b618f6750aed641a23d5f26fbbaf654668846d24/java/core/src/main/java/com/google/protobuf/Utf8.java#L217 private static int encodedUtf8Length(String string) { @@ -376,14 +423,24 @@ private static int encodedUtf8LengthGeneral(String string, int start) { static void writeUtf8( CodedOutputStream output, String string, int utf8Length, MarshalerContext context) throws IOException { - writeUtf8(output, string, utf8Length); + writeUtf8(output, string, utf8Length, context.marshalStringUnsafe()); } // Visible for testing @SuppressWarnings("UnusedVariable") // utf8Length argument is added for future use - static void writeUtf8(CodedOutputStream output, String string, int utf8Length) + static void writeUtf8(CodedOutputStream output, String string, int utf8Length, boolean useUnsafe) throws IOException { - encodeUtf8(output, string); + // if the length of the latin1 string and the utf8 output are the same then the string must be + // composed of only 7bit characters and can be directly copied to the output + if (useUnsafe + && UnsafeString.isAvailable() + && string.length() == utf8Length + && UnsafeString.isLatin1(string)) { + byte[] bytes = UnsafeString.getBytes(string); + output.write(bytes, 0, bytes.length); + } else { + encodeUtf8(output, string); + } } // encode utf8 the same way as length is computed in encodedUtf8Length diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/UnsafeAccess.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/UnsafeAccess.java new file mode 100644 index 00000000000..a57e941d6dd --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/UnsafeAccess.java @@ -0,0 +1,93 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.marshal; + +import io.opentelemetry.api.internal.ConfigUtil; +import java.lang.reflect.Field; +import sun.misc.Unsafe; + +class UnsafeAccess { + private static final int MAX_ENABLED_JAVA_VERSION = 22; + private static final boolean available = checkUnsafe(); + + static boolean isAvailable() { + return available; + } + + private static boolean checkUnsafe() { + double javaVersion = getJavaVersion(); + boolean unsafeEnabled = + Boolean.parseBoolean( + ConfigUtil.getString( + "otel.java.experimental.exporter.unsafe.enabled", + javaVersion != -1 && javaVersion <= MAX_ENABLED_JAVA_VERSION ? "true" : "false")); + if (!unsafeEnabled) { + return false; + } + + try { + Class.forName("sun.misc.Unsafe", false, UnsafeAccess.class.getClassLoader()); + return UnsafeHolder.UNSAFE != null; + } catch (ClassNotFoundException e) { + return false; + } + } + + private static double getJavaVersion() { + String specVersion = System.getProperty("java.specification.version"); + if (specVersion != null) { + try { + return Double.parseDouble(specVersion); + } catch (NumberFormatException exception) { + // ignore + } + } + return -1; + } + + static long objectFieldOffset(Field field) { + return UnsafeHolder.UNSAFE.objectFieldOffset(field); + } + + static Object getObject(Object object, long offset) { + return UnsafeHolder.UNSAFE.getObject(object, offset); + } + + static byte getByte(Object object, long offset) { + return UnsafeHolder.UNSAFE.getByte(object, offset); + } + + static int arrayBaseOffset(Class arrayClass) { + return UnsafeHolder.UNSAFE.arrayBaseOffset(arrayClass); + } + + static long getLong(Object o, long offset) { + return UnsafeHolder.UNSAFE.getLong(o, offset); + } + + private UnsafeAccess() {} + + private static class UnsafeHolder { + public static final Unsafe UNSAFE; + + static { + UNSAFE = getUnsafe(); + } + + private UnsafeHolder() {} + + @SuppressWarnings("NullAway") + private static Unsafe getUnsafe() { + try { + Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + return (Unsafe) field.get(null); + } catch (Exception ignored) { + return null; + } + } + } +} diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/UnsafeString.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/UnsafeString.java new file mode 100644 index 00000000000..c581e7525fb --- /dev/null +++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/marshal/UnsafeString.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.marshal; + +import java.lang.reflect.Field; + +class UnsafeString { + private static final long valueOffset = getStringFieldOffset("value", byte[].class); + private static final long coderOffset = getStringFieldOffset("coder", byte.class); + private static final int byteArrayBaseOffset = UnsafeAccess.arrayBaseOffset(byte[].class); + private static final boolean available = valueOffset != -1 && coderOffset != -1; + + static boolean isAvailable() { + return available; + } + + static boolean isLatin1(String string) { + // 0 represents latin1, 1 utf16 + return UnsafeAccess.getByte(string, coderOffset) == 0; + } + + static byte[] getBytes(String string) { + return (byte[]) UnsafeAccess.getObject(string, valueOffset); + } + + static long getLong(byte[] bytes, int index) { + return UnsafeAccess.getLong(bytes, byteArrayBaseOffset + index); + } + + private static long getStringFieldOffset(String fieldName, Class expectedType) { + if (!UnsafeAccess.isAvailable()) { + return -1; + } + + try { + Field field = String.class.getDeclaredField(fieldName); + if (field.getType() != expectedType) { + return -1; + } + return UnsafeAccess.objectFieldOffset(field); + } catch (Exception exception) { + return -1; + } + } + + private UnsafeString() {} +} diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtilFuzzTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtilFuzzTest.java new file mode 100644 index 00000000000..d8db6d2ed6c --- /dev/null +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtilFuzzTest.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.exporter.internal.marshal; + +import static io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtil.getUtf8Size; +import static io.opentelemetry.exporter.internal.marshal.StatelessMarshalerUtilTest.testUtf8; +import static org.assertj.core.api.Assertions.assertThat; + +import edu.berkeley.cs.jqf.fuzz.Fuzz; +import edu.berkeley.cs.jqf.fuzz.JQF; +import edu.berkeley.cs.jqf.fuzz.junit.GuidedFuzzing; +import edu.berkeley.cs.jqf.fuzz.random.NoGuidance; +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.Test; +import org.junit.runner.Result; +import org.junit.runner.RunWith; + +@SuppressWarnings("SystemOut") +class StatelessMarshalerUtilFuzzTest { + + @RunWith(JQF.class) + public static class EncodeUf8 { + + @Fuzz + public void encodeRandomString(String value) { + int utf8Size = value.getBytes(StandardCharsets.UTF_8).length; + assertThat(getUtf8Size(value, false)).isEqualTo(utf8Size); + assertThat(getUtf8Size(value, true)).isEqualTo(utf8Size); + assertThat(testUtf8(value, utf8Size, /* useUnsafe= */ false)).isEqualTo(value); + assertThat(testUtf8(value, utf8Size, /* useUnsafe= */ true)).isEqualTo(value); + } + } + + // driver methods to avoid having to use the vintage junit engine, and to enable increasing the + // number of iterations: + + @Test + void encodeUf8WithFuzzing() { + Result result = + GuidedFuzzing.run( + EncodeUf8.class, "encodeRandomString", new NoGuidance(10000, System.out), System.out); + assertThat(result.wasSuccessful()).isTrue(); + } +} diff --git a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtilTest.java b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtilTest.java index 6857589822e..c4eec43572d 100644 --- a/exporters/common/src/test/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtilTest.java +++ b/exporters/common/src/test/java/io/opentelemetry/exporter/internal/marshal/StatelessMarshalerUtilTest.java @@ -13,31 +13,33 @@ import java.nio.charset.StandardCharsets; import java.util.Random; import org.junit.jupiter.api.RepeatedTest; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class StatelessMarshalerUtilTest { - @Test + @ParameterizedTest + @ValueSource(strings = {"true", "false"}) @SuppressWarnings("AvoidEscapedUnicodeCharacters") - void encodeUtf8() { - assertThat(getUtf8Size("")).isEqualTo(0); - assertThat(testUtf8("", 0)).isEqualTo(""); + void encodeUtf8(boolean useUnsafe) { + assertThat(getUtf8Size("", useUnsafe)).isEqualTo(0); + assertThat(testUtf8("", 0, useUnsafe)).isEqualTo(""); - assertThat(getUtf8Size("a")).isEqualTo(1); - assertThat(testUtf8("a", 1)).isEqualTo("a"); + assertThat(getUtf8Size("a", useUnsafe)).isEqualTo(1); + assertThat(testUtf8("a", 1, useUnsafe)).isEqualTo("a"); - assertThat(getUtf8Size("©")).isEqualTo(2); - assertThat(testUtf8("©", 2)).isEqualTo("©"); + assertThat(getUtf8Size("©", useUnsafe)).isEqualTo(2); + assertThat(testUtf8("©", 2, useUnsafe)).isEqualTo("©"); - assertThat(getUtf8Size("∆")).isEqualTo(3); - assertThat(testUtf8("∆", 3)).isEqualTo("∆"); + assertThat(getUtf8Size("∆", useUnsafe)).isEqualTo(3); + assertThat(testUtf8("∆", 3, useUnsafe)).isEqualTo("∆"); - assertThat(getUtf8Size("😀")).isEqualTo(4); - assertThat(testUtf8("😀", 4)).isEqualTo("😀"); + assertThat(getUtf8Size("😀", useUnsafe)).isEqualTo(4); + assertThat(testUtf8("😀", 4, useUnsafe)).isEqualTo("😀"); // test that invalid characters are replaced with ? - assertThat(getUtf8Size("\uD83D😀\uDE00")).isEqualTo(6); - assertThat(testUtf8("\uD83D😀\uDE00", 6)).isEqualTo("?😀?"); + assertThat(getUtf8Size("\uD83D😀\uDE00", useUnsafe)).isEqualTo(6); + assertThat(testUtf8("\uD83D😀\uDE00", 6, useUnsafe)).isEqualTo("?😀?"); // the same invalid sequence as encoded by the jdk byte[] bytes = "\uD83D😀\uDE00".getBytes(StandardCharsets.UTF_8); @@ -52,13 +54,13 @@ void testUtf8SizeLatin1() { random.nextBytes(bytes); String string = new String(bytes, StandardCharsets.ISO_8859_1); int utf8Size = string.getBytes(StandardCharsets.UTF_8).length; - assertThat(getUtf8Size(string)).isEqualTo(utf8Size); + assertThat(getUtf8Size(string, true)).isEqualTo(utf8Size); } - private static String testUtf8(String string, int utf8Length) { + static String testUtf8(String string, int utf8Length, boolean useUnsafe) { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream); - writeUtf8(codedOutputStream, string, utf8Length); + writeUtf8(codedOutputStream, string, utf8Length, useUnsafe); codedOutputStream.flush(); return new String(outputStream.toByteArray(), StandardCharsets.UTF_8); } catch (Exception exception) { diff --git a/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/StringMarshalBenchmark.java b/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/StringMarshalBenchmark.java index f375a200185..724f3976cec 100644 --- a/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/StringMarshalBenchmark.java +++ b/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/StringMarshalBenchmark.java @@ -25,80 +25,91 @@ @Measurement(iterations = 10, time = 1) @Fork(1) public class StringMarshalBenchmark { - private static final TestMarshaler MARSHALER = new TestMarshaler(); + private static final TestMarshaler MARSHALER_SAFE = new TestMarshaler(/* useUnsafe= */ false); + private static final TestMarshaler MARSHALER_UNSAFE = new TestMarshaler(/* useUnsafe= */ true); private static final TestOutputStream OUTPUT = new TestOutputStream(); @Benchmark @Threads(1) - public int marshalAsciiString(StringMarshalState state) throws IOException { - OUTPUT.reset(); - Marshaler marshaler = StringAnyValueMarshaler.create(state.asciiString); - marshaler.writeBinaryTo(OUTPUT); - return OUTPUT.getCount(); + public int marshalAsciiStringStateful(StringMarshalState state) throws IOException { + return marshalStateful(state.asciiString); } @Benchmark @Threads(1) - public int marshalLatin1String(StringMarshalState state) throws IOException { - OUTPUT.reset(); - Marshaler marshaler = StringAnyValueMarshaler.create(state.latin1String); - marshaler.writeBinaryTo(OUTPUT); - return OUTPUT.getCount(); + public int marshalLatin1StringStateful(StringMarshalState state) throws IOException { + return marshalStateful(state.latin1String); } @Benchmark @Threads(1) - public int marshalUnicodeString(StringMarshalState state) throws IOException { + public int marshalUnicodeStringStateful(StringMarshalState state) throws IOException { + return marshalStateful(state.unicodeString); + } + + private static int marshalStateful(String string) throws IOException { OUTPUT.reset(); - Marshaler marshaler = StringAnyValueMarshaler.create(state.unicodeString); + Marshaler marshaler = StringAnyValueMarshaler.create(string); marshaler.writeBinaryTo(OUTPUT); return OUTPUT.getCount(); } @Benchmark @Threads(1) - public int marshalAsciiStringLowAllocation(StringMarshalState state) throws IOException { - OUTPUT.reset(); - try { - MARSHALER.initialize(state.asciiString); - MARSHALER.writeBinaryTo(OUTPUT); - return OUTPUT.getCount(); - } finally { - MARSHALER.reset(); - } + public int marshalAsciiStringStatelessSafe(StringMarshalState state) throws IOException { + return marshalStateless(MARSHALER_SAFE, state.asciiString); } @Benchmark @Threads(1) - public int marshalLatin1StringLowAllocation(StringMarshalState state) throws IOException { - OUTPUT.reset(); - try { - MARSHALER.initialize(state.latin1String); - MARSHALER.writeBinaryTo(OUTPUT); - return OUTPUT.getCount(); - } finally { - MARSHALER.reset(); - } + public int marshalAsciiStringStatelessUnsafe(StringMarshalState state) throws IOException { + return marshalStateless(MARSHALER_UNSAFE, state.asciiString); + } + + @Benchmark + @Threads(1) + public int marshalLatin1StringStatelessSafe(StringMarshalState state) throws IOException { + return marshalStateless(MARSHALER_SAFE, state.latin1String); + } + + @Benchmark + @Threads(1) + public int marshalLatin1StringStatelessUnsafe(StringMarshalState state) throws IOException { + return marshalStateless(MARSHALER_UNSAFE, state.latin1String); } @Benchmark @Threads(1) - public int marshalUnicodeStringLowAllocation(StringMarshalState state) throws IOException { + public int marshalUnicodeStringStatelessSafe(StringMarshalState state) throws IOException { + return marshalStateless(MARSHALER_SAFE, state.unicodeString); + } + + @Benchmark + @Threads(1) + public int marshalUnicodeStringStatelessUnsafe(StringMarshalState state) throws IOException { + return marshalStateless(MARSHALER_UNSAFE, state.unicodeString); + } + + private static int marshalStateless(TestMarshaler marshaler, String string) throws IOException { OUTPUT.reset(); try { - MARSHALER.initialize(state.unicodeString); - MARSHALER.writeBinaryTo(OUTPUT); + marshaler.initialize(string); + marshaler.writeBinaryTo(OUTPUT); return OUTPUT.getCount(); } finally { - MARSHALER.reset(); + marshaler.reset(); } } private static class TestMarshaler extends Marshaler { - private final MarshalerContext context = new MarshalerContext(); + private final MarshalerContext context; private int size; private String value; + TestMarshaler(boolean useUnsafe) { + context = new MarshalerContext(/* marshalStringNoAllocation= */ true, useUnsafe); + } + public void initialize(String string) { value = string; size = StringAnyValueStatelessMarshaler.INSTANCE.getBinarySerializedSize(string, context); diff --git a/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/StringMarshalState.java b/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/StringMarshalState.java index 684b6c65080..4d7e3aacd18 100644 --- a/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/StringMarshalState.java +++ b/exporters/otlp/common/src/jmh/java/io/opentelemetry/exporter/internal/otlp/StringMarshalState.java @@ -13,7 +13,7 @@ @State(Scope.Benchmark) public class StringMarshalState { - @Param({"16", "512"}) + @Param("512") int stringSize; String asciiString; diff --git a/settings.gradle.kts b/settings.gradle.kts index a5d95eb9bf7..5a2959dc4e6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,6 +32,7 @@ include(":dependencyManagement") include(":extensions:kotlin") include(":extensions:trace-propagators") include(":exporters:common") +include(":exporters:common:compile-stub") include(":exporters:sender:grpc-managed-channel") include(":exporters:sender:jdk") include(":exporters:sender:okhttp")