diff --git a/components/json/build.gradle.kts b/components/json/build.gradle.kts index b8314ee7f33..4dca7fc3036 100644 --- a/components/json/build.gradle.kts +++ b/components/json/build.gradle.kts @@ -1 +1,9 @@ +plugins { + id("me.champeau.jmh") +} + apply(from = "$rootDir/gradle/java.gradle") + +jmh { + version = "1.28" +} diff --git a/components/json/src/jmh/java/datadog/json/JsonWriterBenchmark.java b/components/json/src/jmh/java/datadog/json/JsonWriterBenchmark.java new file mode 100644 index 00000000000..6e9adc62efc --- /dev/null +++ b/components/json/src/jmh/java/datadog/json/JsonWriterBenchmark.java @@ -0,0 +1,106 @@ +package datadog.json; + +import static java.util.concurrent.TimeUnit.MICROSECONDS; +import static org.openjdk.jmh.annotations.Mode.AverageTime; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.infra.Blackhole; + +@BenchmarkMode(AverageTime) +@OutputTimeUnit(MICROSECONDS) +@Fork(value = 1) +@SuppressWarnings("unused") +public class JsonWriterBenchmark { + @Benchmark + public void writeSimpleArray(Blackhole blackhole) { + try (JsonWriter writer = new JsonWriter()) { + writer + .beginArray() + .beginObject() + .name("true") + .value(true) + .endObject() + .beginObject() + .name("false") + .value(false) + .endObject() + .endArray(); + blackhole.consume(writer.toString()); + } + } + + @Benchmark + public void writeComplexArray(Blackhole blackhole) { + try (JsonWriter writer = new JsonWriter()) { + writer + .beginArray() + .value("first level") + .beginArray() + .value("second level") + .beginArray() + .value("third level") + .beginObject() + .name("key") + .value("value") + .endObject() + .beginObject() + .name("key") + .value("value") + .endObject() + .endArray() // second level + .beginObject() + .name("key") + .value("value") + .endObject() + .endArray() // first level + .beginObject() + .name("key") + .value("value") + .endObject() + .value("last value") + .endArray(); + blackhole.consume(writer.toString()); + } + } + + @Benchmark + public void writeComplexObject(Blackhole blackhole) { + try (JsonWriter writer = new JsonWriter()) { + writer + .beginObject() + .name("attrs") + .beginObject() + .name("attr1") + .value("value1") + .name("attr2") + .value("value2") + .endObject() + .name("data") + .beginArray() + .beginObject() + .name("x") + .value(1) + .name("y") + .value(12.3) + .endObject() + .beginObject() + .name("x") + .value(2) + .name("y") + .value(4.56) + .endObject() + .beginObject() + .name("x") + .value(3) + .name("y") + .value(789) + .endObject() + .endArray() + .endObject(); + blackhole.consume(writer.toString()); + } + } +} diff --git a/components/json/src/main/java/datadog/json/SafeJsonStructure.java b/components/json/src/main/java/datadog/json/SafeJsonStructure.java index 950b1f3ab18..d6ac0adabf5 100644 --- a/components/json/src/main/java/datadog/json/SafeJsonStructure.java +++ b/components/json/src/main/java/datadog/json/SafeJsonStructure.java @@ -1,20 +1,19 @@ package datadog.json; -import static java.util.stream.Collectors.joining; - -import java.util.ArrayDeque; -import java.util.Deque; +import java.util.BitSet; /** * This {@link JsonStructure} performs minimal structure checks to ensure the built JSON is * coherent. */ class SafeJsonStructure implements JsonStructure { - private final Deque structure; + private final BitSet structure; + private int depth; private boolean complete; SafeJsonStructure() { - this.structure = new ArrayDeque<>(); + this.structure = new BitSet(); + this.depth = -1; this.complete = false; } @@ -23,12 +22,12 @@ public void beginObject() { if (this.complete) { throw new IllegalStateException("Object is complete"); } - this.structure.add(true); + this.structure.set(++this.depth); } @Override public boolean objectStarted() { - return !this.structure.isEmpty() && this.structure.peekLast(); + return this.depth >= 0 && this.structure.get(this.depth); } @Override @@ -36,8 +35,8 @@ public void endObject() { if (!objectStarted()) { throw new IllegalStateException("Object not started"); } - this.structure.removeLast(); - if (this.structure.isEmpty()) { + this.depth--; + if (this.depth < 0) { this.complete = true; } } @@ -47,12 +46,12 @@ public void beginArray() { if (this.complete) { throw new IllegalStateException("Object is complete"); } - this.structure.offer(false); + this.structure.clear(++this.depth); } @Override public boolean arrayStarted() { - return !this.structure.isEmpty() && !this.structure.peekLast(); + return this.depth >= 0 && !this.structure.get(this.depth); } @Override @@ -60,8 +59,8 @@ public void endArray() { if (!arrayStarted()) { throw new IllegalStateException("Array not started"); } - this.structure.removeLast(); - if (this.structure.isEmpty()) { + this.depth--; + if (this.depth < 0) { this.complete = true; } } @@ -78,16 +77,13 @@ public void addValue() { if (this.complete) { throw new IllegalStateException("Object is complete"); } - if (this.structure.isEmpty()) { + if (this.depth < 0) { this.complete = true; } } @Override public String toString() { - return (this.complete ? "complete" : "") - + this.structure.stream() - .map(b -> b ? "object start" : "array start") - .collect(joining(",")); + return (this.complete ? "complete" : "") + this.structure; } }