Skip to content

Commit 7c3d6e2

Browse files
committed
feat(json): Improve structure performance
1 parent 1776ab7 commit 7c3d6e2

File tree

3 files changed

+129
-19
lines changed

3 files changed

+129
-19
lines changed

components/json/build.gradle.kts

+8
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
1+
plugins {
2+
id("me.champeau.jmh")
3+
}
4+
15
apply(from = "$rootDir/gradle/java.gradle")
6+
7+
jmh {
8+
version = "1.28"
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package datadog.json;
2+
3+
import static java.util.concurrent.TimeUnit.MICROSECONDS;
4+
import static org.openjdk.jmh.annotations.Mode.AverageTime;
5+
6+
import org.openjdk.jmh.annotations.Benchmark;
7+
import org.openjdk.jmh.annotations.BenchmarkMode;
8+
import org.openjdk.jmh.annotations.Fork;
9+
import org.openjdk.jmh.annotations.OutputTimeUnit;
10+
import org.openjdk.jmh.infra.Blackhole;
11+
12+
@BenchmarkMode(AverageTime)
13+
@OutputTimeUnit(MICROSECONDS)
14+
@Fork(value = 1)
15+
@SuppressWarnings("unused")
16+
public class JsonWriterBenchmark {
17+
@Benchmark
18+
public void writeSimpleArray(Blackhole blackhole) {
19+
try (JsonWriter writer = new JsonWriter()) {
20+
writer
21+
.beginArray()
22+
.beginObject()
23+
.name("true")
24+
.value(true)
25+
.endObject()
26+
.beginObject()
27+
.name("false")
28+
.value(false)
29+
.endObject()
30+
.endArray();
31+
blackhole.consume(writer.toString());
32+
}
33+
}
34+
35+
@Benchmark
36+
public void writeComplexArray(Blackhole blackhole) {
37+
try (JsonWriter writer = new JsonWriter()) {
38+
writer
39+
.beginArray()
40+
.value("first level")
41+
.beginArray()
42+
.value("second level")
43+
.beginArray()
44+
.value("third level")
45+
.beginObject()
46+
.name("key")
47+
.value("value")
48+
.endObject()
49+
.beginObject()
50+
.name("key")
51+
.value("value")
52+
.endObject()
53+
.endArray() // second level
54+
.beginObject()
55+
.name("key")
56+
.value("value")
57+
.endObject()
58+
.endArray() // first level
59+
.beginObject()
60+
.name("key")
61+
.value("value")
62+
.endObject()
63+
.value("last value")
64+
.endArray();
65+
blackhole.consume(writer.toString());
66+
}
67+
}
68+
69+
@Benchmark
70+
public void writeComplexObject(Blackhole blackhole) {
71+
try (JsonWriter writer = new JsonWriter()) {
72+
writer
73+
.beginObject()
74+
.name("attrs")
75+
.beginObject()
76+
.name("attr1")
77+
.value("value1")
78+
.name("attr2")
79+
.value("value2")
80+
.endObject()
81+
.name("data")
82+
.beginArray()
83+
.beginObject()
84+
.name("x")
85+
.value(1)
86+
.name("y")
87+
.value(12.3)
88+
.endObject()
89+
.beginObject()
90+
.name("x")
91+
.value(2)
92+
.name("y")
93+
.value(4.56)
94+
.endObject()
95+
.beginObject()
96+
.name("x")
97+
.value(3)
98+
.name("y")
99+
.value(789)
100+
.endObject()
101+
.endArray()
102+
.endObject();
103+
blackhole.consume(writer.toString());
104+
}
105+
}
106+
}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
package datadog.json;
22

3-
import static java.util.stream.Collectors.joining;
4-
5-
import java.util.ArrayDeque;
6-
import java.util.Deque;
3+
import java.util.BitSet;
74

85
/**
96
* This {@link JsonStructure} performs minimal structure checks to ensure the built JSON is
107
* coherent.
118
*/
129
class SafeJsonStructure implements JsonStructure {
13-
private final Deque<Boolean> structure;
10+
private final BitSet structure;
11+
private int depth;
1412
private boolean complete;
1513

1614
SafeJsonStructure() {
17-
this.structure = new ArrayDeque<>();
15+
this.structure = new BitSet();
16+
this.depth = -1;
1817
this.complete = false;
1918
}
2019

@@ -23,21 +22,21 @@ public void beginObject() {
2322
if (this.complete) {
2423
throw new IllegalStateException("Object is complete");
2524
}
26-
this.structure.add(true);
25+
this.structure.set(++this.depth);
2726
}
2827

2928
@Override
3029
public boolean objectStarted() {
31-
return !this.structure.isEmpty() && this.structure.peekLast();
30+
return this.depth >= 0 && this.structure.get(this.depth);
3231
}
3332

3433
@Override
3534
public void endObject() {
3635
if (!objectStarted()) {
3736
throw new IllegalStateException("Object not started");
3837
}
39-
this.structure.removeLast();
40-
if (this.structure.isEmpty()) {
38+
this.depth--;
39+
if (this.depth < 0) {
4140
this.complete = true;
4241
}
4342
}
@@ -47,21 +46,21 @@ public void beginArray() {
4746
if (this.complete) {
4847
throw new IllegalStateException("Object is complete");
4948
}
50-
this.structure.offer(false);
49+
this.structure.clear(++this.depth);
5150
}
5251

5352
@Override
5453
public boolean arrayStarted() {
55-
return !this.structure.isEmpty() && !this.structure.peekLast();
54+
return this.depth >= 0 && !this.structure.get(this.depth);
5655
}
5756

5857
@Override
5958
public void endArray() {
6059
if (!arrayStarted()) {
6160
throw new IllegalStateException("Array not started");
6261
}
63-
this.structure.removeLast();
64-
if (this.structure.isEmpty()) {
62+
this.depth--;
63+
if (this.depth < 0) {
6564
this.complete = true;
6665
}
6766
}
@@ -78,16 +77,13 @@ public void addValue() {
7877
if (this.complete) {
7978
throw new IllegalStateException("Object is complete");
8079
}
81-
if (this.structure.isEmpty()) {
80+
if (this.depth < 0) {
8281
this.complete = true;
8382
}
8483
}
8584

8685
@Override
8786
public String toString() {
88-
return (this.complete ? "complete" : "")
89-
+ this.structure.stream()
90-
.map(b -> b ? "object start" : "array start")
91-
.collect(joining(","));
87+
return (this.complete ? "complete" : "") + this.structure;
9288
}
9389
}

0 commit comments

Comments
 (0)