From 4d37c54e4d78b6865e4d49be0b0936bb236fd18b Mon Sep 17 00:00:00 2001 From: David Schlosnagle Date: Thu, 10 Aug 2023 00:01:51 -0400 Subject: [PATCH 1/5] JsonArbitraryFieldNameBenchmark uses Supplier --- .../json/JsonArbitraryFieldNameBenchmark.java | 53 ++++++++++++++----- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java b/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java index 0ec0a38..804d1d3 100644 --- a/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java +++ b/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java @@ -18,6 +18,9 @@ import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -51,7 +54,8 @@ > B apply(B factory) { > B apply(B factory) { return factory.disable(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES); } - }; + }, + ; abstract > B apply(B factory); } @@ -62,38 +66,47 @@ > B apply(B factory) { public enum InputType { INPUT_STREAM() { @Override - JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOException { - return factory.createParser(new ByteArrayInputStream(jsonSupplier.get().getBytes(StandardCharsets.UTF_8))); + JsonParser create(JsonFactory factory, Supplier bytesSupplier) throws IOException { + return factory.createParser(new ByteArrayInputStream(bytesSupplier.get())); } }, READER() { @Override - JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOException { - // Instead of using 'new StringReader(jsonSupplier.get())', we construct an InputStreamReader + JsonParser create(JsonFactory factory, Supplier bytesSupplier) throws IOException { + // Instead of using 'new StringReader(bytesSupplier.get())', we construct an InputStreamReader // to more closely match overhead of INPUT_STREAM for comparison. return factory.createParser(new InputStreamReader( - new ByteArrayInputStream(jsonSupplier.get().getBytes(StandardCharsets.UTF_8)), + new ByteArrayInputStream(bytesSupplier.get()), StandardCharsets.UTF_8)); } - }; + }, + ; - abstract JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOException; + abstract JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOException; } public enum InputShape { + KEY_MAP( + new TypeReference>() { + }, + () -> "{\"key\":true}"), RANDOM_KEY_MAP( - new TypeReference>() {}, + new TypeReference>() { + }, () -> "{\"" + ThreadLocalRandom.current().nextInt() + "\":true}"), BEAN_WITH_RANDOM_KEY_MAP( - new TypeReference() {}, + new TypeReference() { + }, () -> "{\"fieldWithMap\":{\"" + ThreadLocalRandom.current().nextInt() - + "\":true},\"stringOne\":\"a\",\"stringTwo\":\"a\",\"stringThree\":\"a\"}"); + + "\":true},\"stringOne\":\"a\",\"stringTwo\":\"a\",\"stringThree\":\"a\"}"), + ; private final TypeReference typereference; - private final Supplier jsonSupplier; + private final Supplier bytesSupplier; + InputShape(TypeReference typereference, Supplier jsonSupplier) { this.typereference = typereference; - this.jsonSupplier = jsonSupplier; + this.bytesSupplier = () -> jsonSupplier.get().getBytes(StandardCharsets.UTF_8); } } @@ -121,7 +134,7 @@ public void setup() { @Benchmark public Object parse() throws IOException { - try (JsonParser parser = type.create(factory, shape.jsonSupplier)) { + try (JsonParser parser = type.create(factory, shape.bytesSupplier)) { return reader.readValue(parser); } } @@ -140,4 +153,16 @@ public static final class SimpleClass { @JsonProperty("stringThree") public String stringThree; } + + public static void main(String[] _args) throws Exception { + new Runner(new OptionsBuilder() + .include(JsonArbitraryFieldNameBenchmark.class.getName()) + .warmupIterations(2) + .warmupTime(TimeValue.seconds(5)) + .measurementIterations(4) + .measurementTime(TimeValue.seconds(5)) + .mode(Mode.AverageTime) + .forks(1) + .build()).run(); + } } From b071a6bb0f7e14cd72a7c666f70922c6101a104b Mon Sep 17 00:00:00 2001 From: David Schlosnagle Date: Thu, 10 Aug 2023 00:05:09 -0400 Subject: [PATCH 2/5] Add CharBufferReader benchmark --- .../json/JsonArbitraryFieldNameBenchmark.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java b/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java index 804d1d3..960e1e3 100644 --- a/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java +++ b/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java @@ -25,6 +25,10 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; @@ -80,6 +84,15 @@ JsonParser create(JsonFactory factory, Supplier bytesSupplier) throws IO StandardCharsets.UTF_8)); } }, + CHAR_READER() { + @Override + JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOException { + ByteBuffer byteBuffer = ByteBuffer.wrap(jsonSupplier.get()); + CharBuffer charBuffer = Charset.forName("UTF-8").decode(byteBuffer); + CharBufferReader charBufferReader = new CharBufferReader(charBuffer); + return factory.createParser(charBufferReader); + } + }, ; abstract JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOException; @@ -154,6 +167,68 @@ public static final class SimpleClass { public String stringThree; } + public static class CharBufferReader extends Reader { + private final CharBuffer charBuffer; + + public CharBufferReader(CharBuffer buffer) { + this.charBuffer = buffer.duplicate(); + } + + @Override + public int read(char[] chars, int off, int len) { + int remaining = this.charBuffer.remaining(); + if (remaining <= 0) { + return -1; + } + int length = Math.min(len, remaining); + this.charBuffer.get(chars, off, length); + return length; + } + + @Override + public int read() { + if (this.charBuffer.hasRemaining()) { + return this.charBuffer.get(); + } + return -1; + } + + @Override + public long skip(long n) { + if (n < 0L) { + throw new IllegalArgumentException("number of characters to skip cannot be negative"); + } + int skipped = Math.min((int) n, this.charBuffer.remaining()); + this.charBuffer.position(this.charBuffer.position() + skipped); + return skipped; + } + + @Override + public boolean ready() { + return true; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public void mark(int readAheadLimit) { + this.charBuffer.mark(); + } + + @Override + public void reset() { + this.charBuffer.reset(); + } + + @Override + public void close() { + this.charBuffer.position(this.charBuffer.limit()); + } + } + public static void main(String[] _args) throws Exception { new Runner(new OptionsBuilder() .include(JsonArbitraryFieldNameBenchmark.class.getName()) From 8821d54cdb4784ba5dc1589d78bf77ba8d4565c8 Mon Sep 17 00:00:00 2001 From: David Schlosnagle Date: Thu, 10 Aug 2023 15:54:36 -0400 Subject: [PATCH 3/5] Add StringReader to JsonArbitraryFieldNameBenchmark --- .../perf/json/JsonArbitraryFieldNameBenchmark.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java b/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java index 960e1e3..233225c 100644 --- a/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java +++ b/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import java.io.StringReader; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; @@ -93,6 +94,13 @@ JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOE return factory.createParser(charBufferReader); } }, + STRING_READER() { + @Override + JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOException { + StringReader reader = new StringReader(new String(jsonSupplier.get(), Charset.forName("UTF-8"))); + return factory.createParser(reader); + } + }, ; abstract JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOException; From 49100a7f627909aea8c4d303360c82e6f19f42d6 Mon Sep 17 00:00:00 2001 From: David Schlosnagle Date: Thu, 10 Aug 2023 15:55:19 -0400 Subject: [PATCH 4/5] Add BEAN_WITH_LARGE_KEY_MAP to JsonArbitraryFieldNameBenchmark --- .../json/JsonArbitraryFieldNameBenchmark.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java b/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java index 233225c..2d3c0a4 100644 --- a/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java +++ b/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java @@ -120,8 +120,33 @@ public enum InputShape { }, () -> "{\"fieldWithMap\":{\"" + ThreadLocalRandom.current().nextInt() + "\":true},\"stringOne\":\"a\",\"stringTwo\":\"a\",\"stringThree\":\"a\"}"), + BEAN_WITH_LARGE_KEY_MAP( + new TypeReference() { + }, + new Supplier() { + private final String json = generateSimpleInstanceJson(10_000); + + @Override + public String get() { + return json; + } + } + ), ; + private static String generateSimpleInstanceJson(int n) { + StringBuilder builder = new StringBuilder(); + builder.append("{\"fieldWithMap\":{"); + for (int i = 0; i < n; i++) { + builder.append("\"").append(i).append("\":").append(i % 2 == 0); + if (i < n-1) { + builder.append(','); + } + } + builder.append("},\"stringOne\":\"a\",\"stringTwo\":\"a\",\"stringThree\":\"a\"}"); + return builder.toString(); + } + private final TypeReference typereference; private final Supplier bytesSupplier; From 2f689f6a4c9f23a59b9779e3958ccc0b5bf927bc Mon Sep 17 00:00:00 2001 From: David Schlosnagle Date: Sat, 19 Aug 2023 09:19:35 -0400 Subject: [PATCH 5/5] Remove CharBufferReader from JsonArbitraryFieldNameBenchmark --- .../json/JsonArbitraryFieldNameBenchmark.java | 74 ------------------- 1 file changed, 74 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java b/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java index 2d3c0a4..51ff20b 100644 --- a/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java +++ b/src/main/java/com/fasterxml/jackson/perf/json/JsonArbitraryFieldNameBenchmark.java @@ -25,10 +25,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.io.Reader; import java.io.StringReader; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Map; @@ -85,15 +82,6 @@ JsonParser create(JsonFactory factory, Supplier bytesSupplier) throws IO StandardCharsets.UTF_8)); } }, - CHAR_READER() { - @Override - JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOException { - ByteBuffer byteBuffer = ByteBuffer.wrap(jsonSupplier.get()); - CharBuffer charBuffer = Charset.forName("UTF-8").decode(byteBuffer); - CharBufferReader charBufferReader = new CharBufferReader(charBuffer); - return factory.createParser(charBufferReader); - } - }, STRING_READER() { @Override JsonParser create(JsonFactory factory, Supplier jsonSupplier) throws IOException { @@ -200,68 +188,6 @@ public static final class SimpleClass { public String stringThree; } - public static class CharBufferReader extends Reader { - private final CharBuffer charBuffer; - - public CharBufferReader(CharBuffer buffer) { - this.charBuffer = buffer.duplicate(); - } - - @Override - public int read(char[] chars, int off, int len) { - int remaining = this.charBuffer.remaining(); - if (remaining <= 0) { - return -1; - } - int length = Math.min(len, remaining); - this.charBuffer.get(chars, off, length); - return length; - } - - @Override - public int read() { - if (this.charBuffer.hasRemaining()) { - return this.charBuffer.get(); - } - return -1; - } - - @Override - public long skip(long n) { - if (n < 0L) { - throw new IllegalArgumentException("number of characters to skip cannot be negative"); - } - int skipped = Math.min((int) n, this.charBuffer.remaining()); - this.charBuffer.position(this.charBuffer.position() + skipped); - return skipped; - } - - @Override - public boolean ready() { - return true; - } - - @Override - public boolean markSupported() { - return true; - } - - @Override - public void mark(int readAheadLimit) { - this.charBuffer.mark(); - } - - @Override - public void reset() { - this.charBuffer.reset(); - } - - @Override - public void close() { - this.charBuffer.position(this.charBuffer.limit()); - } - } - public static void main(String[] _args) throws Exception { new Runner(new OptionsBuilder() .include(JsonArbitraryFieldNameBenchmark.class.getName())