From 428c4a68d56a09e209f443bc9b4eb351e5b1957e Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Sat, 21 Sep 2019 16:15:45 +0100 Subject: [PATCH] added benchmark subproject --- .../serialization/json/PerformanceTest.java | 1 + .../net/woggioni/worth/benchmark/Main.java | 176 ++++++++++++++++++ build.sbt | 27 ++- .../worth/serialization/json/JSONParser.java | 4 +- .../woggioni/worth/utils}/Chronometer.java | 2 +- .../net/woggioni/worth/value/ObjectValue.java | 2 +- .../java/net/woggioni/worth/xface/Value.java | 4 - 7 files changed, 205 insertions(+), 11 deletions(-) create mode 100644 benchmark/src/main/java/net/woggioni/worth/benchmark/Main.java rename src/{test/java/net/woggioni/worth/serialization/json => main/java/net/woggioni/worth/utils}/Chronometer.java (92%) diff --git a/antlr/src/test/java/net/woggioni/worth/serialization/json/PerformanceTest.java b/antlr/src/test/java/net/woggioni/worth/serialization/json/PerformanceTest.java index 7ccd1ec..533abb9 100644 --- a/antlr/src/test/java/net/woggioni/worth/serialization/json/PerformanceTest.java +++ b/antlr/src/test/java/net/woggioni/worth/serialization/json/PerformanceTest.java @@ -7,6 +7,7 @@ import net.woggioni.worth.antlr.JSONLexer; import net.woggioni.worth.antlr.JSONListenerImpl; import net.woggioni.worth.serialization.binary.JBONDumper; import net.woggioni.worth.serialization.binary.JBONParser; +import net.woggioni.worth.utils.Chronometer; import net.woggioni.worth.value.ObjectValue; import net.woggioni.worth.xface.Dumper; import net.woggioni.worth.xface.Parser; diff --git a/benchmark/src/main/java/net/woggioni/worth/benchmark/Main.java b/benchmark/src/main/java/net/woggioni/worth/benchmark/Main.java new file mode 100644 index 0000000..5319105 --- /dev/null +++ b/benchmark/src/main/java/net/woggioni/worth/benchmark/Main.java @@ -0,0 +1,176 @@ +package net.woggioni.worth.benchmark; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; +import net.woggioni.worth.antlr.JSONLexer; +import net.woggioni.worth.antlr.JSONListenerImpl; +import net.woggioni.worth.serialization.binary.JBONParser; +import net.woggioni.worth.serialization.json.JSONParser; +import net.woggioni.worth.utils.Chronometer; +import net.woggioni.worth.value.ObjectValue; +import net.woggioni.worth.xface.Value; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.tukaani.xz.XZInputStream; + +import java.io.*; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class Main { + + @SneakyThrows + private static InputStream extractTestData() { + return new XZInputStream(new BufferedInputStream(Main.class.getResourceAsStream("/citylots.json.xz"))); + } + + @SneakyThrows + private static InputStream extractBinaryTestData() { + return new XZInputStream(new BufferedInputStream(Main.class.getResourceAsStream("/citylots.jbon.xz"))); + } + + private static InputStream smallTestData() { + return new BufferedInputStream(Main.class.getResourceAsStream("/wordpress.json")); + } + + @SneakyThrows + private static void loopBenchmark() { + double jacksonTime, worthTime, antlrTime; + final int loops = 100; + Chronometer chr = new Chronometer(); + { + ObjectMapper om = new ObjectMapper(); + for (int j = 0; j < 2; j++) { + chr.reset(); + for (int i = 0; i < loops; i++) { + JsonNode jsonNode = om.readTree(smallTestData()); + } + } + jacksonTime = chr.stop(Chronometer.TimeUnit.MILLISECOND); + System.out.printf("Jackson time: %8s msec\n", String.format("%.3f", jacksonTime)); + } + { + for (int j = 0; j < 2; j++) { + chr.reset(); + for (int i = 0; i < loops; i++) { + Value value = new JSONParser().parse(smallTestData()); + } + } + worthTime = chr.stop(Chronometer.TimeUnit.MILLISECOND); + System.out.printf("Worth time: %8s msec\n", String.format("%.3f", worthTime)); + } + { + for (int j = 0; j < 2; j++) { + chr.reset(); + for (int i = 0; i < loops; i++) { + CharStream inputStream = CharStreams.fromReader(new InputStreamReader(smallTestData())); + JSONLexer lexer = new JSONLexer(inputStream); + CommonTokenStream commonTokenStream = new CommonTokenStream(lexer); + net.woggioni.worth.antlr.JSONParser parser = new net.woggioni.worth.antlr.JSONParser(commonTokenStream); + JSONListenerImpl listener = new JSONListenerImpl(); + ParseTreeWalker walker = new ParseTreeWalker(); + walker.walk(listener, parser.json()); + } + } + antlrTime = chr.stop(Chronometer.TimeUnit.MILLISECOND); + System.out.printf("Antlr time: %8s msec\n", String.format("%.3f", antlrTime)); + } + } + + private static Value.Configuration buildConfiguration() { + return Value.Configuration.builder() + .objectValueImplementation(ObjectValue.Implementation.ArrayList) + .build(); + } + + @SneakyThrows + public static void jacksonBenchmark() { + Chronometer chr = new Chronometer(); + try (InputStream is = extractTestData()) { + chr.reset(); + ObjectMapper om = new ObjectMapper(); + om.readTree(is); + double elapsedTime = chr.stop(Chronometer.TimeUnit.SECOND); + System.out.printf("Jackson time: %8s sec\n", String.format("%.3f", elapsedTime)); + } + } + + @SneakyThrows + public static void worthJsonBenchmark() { + Chronometer chr = new Chronometer(); + try (InputStream is = extractTestData()) { + chr.reset(); + new JSONParser(buildConfiguration()).parse(is); + double elapsedTime = chr.stop(Chronometer.TimeUnit.SECOND); + System.out.printf("Worth json time: %8s sec\n", String.format("%.3f", elapsedTime)); + } + } + + @SneakyThrows + public static void worthJbonBenchmark() { + Chronometer chr = new Chronometer(); + try (InputStream is = extractBinaryTestData()) { + chr.reset(); + new JBONParser(buildConfiguration()).parse(is); + double elapsedTime = chr.stop(Chronometer.TimeUnit.SECOND); + System.out.printf("Worth jbon time: %8s sec\n", String.format("%.3f", elapsedTime)); + } + } + + @SneakyThrows + public static void antlrBenchmark() { + Chronometer chr = new Chronometer(); + try (InputStream is = extractTestData()) { + chr.reset(); + CharStream inputStream = CharStreams.fromReader(new InputStreamReader(is)); + JSONLexer lexer = new JSONLexer(inputStream); + CommonTokenStream commonTokenStream = new CommonTokenStream(lexer); + net.woggioni.worth.antlr.JSONParser parser = new net.woggioni.worth.antlr.JSONParser(commonTokenStream); + JSONListenerImpl listener = new JSONListenerImpl(); + ParseTreeWalker walker = new ParseTreeWalker(); + walker.walk(listener, parser.json()); + double elapsedTime = chr.stop(Chronometer.TimeUnit.SECOND); + System.out.printf("Antlr time: %8s sec\n", String.format("%.3f", elapsedTime)); + } + } + + public static void main(String[] args) { + if(args.length == 0) { + System.out.println("Benchmark names expected as command line arguments"); + System.exit(-1); + } + Method[] methods = Main.class.getMethods(); + for(String benchmarkName : args) { + Optional targetMethod = Arrays.stream(methods) + .filter(method -> Objects.equals(benchmarkName, method.getName())) + .findFirst(); + targetMethod.ifPresent(new Consumer<>() { + @Override + @SneakyThrows + public void accept(Method method) { + method.invoke(null); + } + }); + targetMethod.orElseThrow(() -> { + List benchmarkNames = Arrays.stream(methods) + .filter(m -> m.getName() + .endsWith("Benchmark")) + .map(Method::getName) + .collect(Collectors.toList()); + String msg = "Unknown benchmark '" + benchmarkName + + "', available benchmarks are: " + + String.join(", ", benchmarkNames); + return new IllegalArgumentException(msg); + }); + } + } +} diff --git a/build.sbt b/build.sbt index 24305a8..1f23c67 100644 --- a/build.sbt +++ b/build.sbt @@ -15,7 +15,6 @@ scalacOptions ++= Seq( ) git.useGitDescribe := true -fork := true //javaOptions in Test += "-Xmx14G" //scalafmtOnCompile := true libraryDependencies += "org.projectlombok" % "lombok" % "1.18.8" % Provided @@ -36,10 +35,13 @@ lazy val worthAntlr = (project in file("antlr")).settings( antlr4PackageName in Antlr4 := Some("net.woggioni.worth.antlr"), skip in publish := true, unmanagedClasspath in Test += (classDirectory in (LocalRootProject, Test)).value, + unmanagedClasspath in Runtime += (resourceDirectory in (LocalRootProject, Test)).value, + libraryDependencies += "com.fasterxml.jackson.core" % "jackson-databind" % "2.9.6" % Test, + libraryDependencies += "org.tukaani" % "xz" % "1.8" % Test, libraryDependencies += "org.antlr" % "antlr4" % antlrVersion % Test, libraryDependencies += "org.antlr" % "antlr4-runtime" % antlrVersion % Test, + libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test, libraryDependencies += "org.projectlombok" % "lombok" % "1.18.8" % Provided, - libraryDependencies ++= testDependencies ).dependsOn(LocalRootProject).enablePlugins(Antlr4Plugin) lazy val cli = (project in file("cli")).settings( @@ -52,4 +54,23 @@ lazy val cli = (project in file("cli")).settings( maintainer := "oggioni.walter@gmail.com", unmanagedClasspath in Test += (classDirectory in (LocalRootProject, Test)).value, libraryDependencies += "com.beust" % "jcommander" % "1.72" -).dependsOn(LocalRootProject).enablePlugins(JavaAppPackaging).enablePlugins(UniversalPlugin) \ No newline at end of file +).dependsOn(LocalRootProject).enablePlugins(JavaAppPackaging).enablePlugins(UniversalPlugin) + +lazy val benchmark = (project in file("benchmark")).settings( + organization := (organization in LocalRootProject).value, + name := "worth-benchmark", + version := (version in LocalRootProject).value, + resourceDirectory in Compile := (resourceDirectory in(LocalRootProject, Test)).value, + skip in publish := true, + maintainer := "oggioni.walter@gmail.com", + mainClass := Some("net.woggioni.worth.benchmark.Main"), + javaOptions in Universal += "-J-Xmx4G", + fork := true, + libraryDependencies += "com.fasterxml.jackson.core" % "jackson-databind" % "2.9.6", + libraryDependencies += "org.tukaani" % "xz" % "1.8", + libraryDependencies += "com.beust" % "jcommander" % "1.72", + libraryDependencies += "org.projectlombok" % "lombok" % "1.18.8" % Provided +).dependsOn(LocalRootProject) + .dependsOn(worthAntlr) + .enablePlugins(JavaAppPackaging) + .enablePlugins(UniversalPlugin) diff --git a/src/main/java/net/woggioni/worth/serialization/json/JSONParser.java b/src/main/java/net/woggioni/worth/serialization/json/JSONParser.java index 7df1cc5..c8d2826 100644 --- a/src/main/java/net/woggioni/worth/serialization/json/JSONParser.java +++ b/src/main/java/net/woggioni/worth/serialization/json/JSONParser.java @@ -218,9 +218,9 @@ public class JSONParser extends ValueParser { try { String text = parseNumber(stream); if (text.indexOf('.') > 0) { - floatValue(Double.valueOf(text)); + floatValue(Double.parseDouble(text)); } else { - integerValue(Long.valueOf(text)); + integerValue(Long.parseLong(text)); } continue; } catch (NumberFormatException nfe) { diff --git a/src/test/java/net/woggioni/worth/serialization/json/Chronometer.java b/src/main/java/net/woggioni/worth/utils/Chronometer.java similarity index 92% rename from src/test/java/net/woggioni/worth/serialization/json/Chronometer.java rename to src/main/java/net/woggioni/worth/utils/Chronometer.java index b04d56d..30607d2 100644 --- a/src/test/java/net/woggioni/worth/serialization/json/Chronometer.java +++ b/src/main/java/net/woggioni/worth/utils/Chronometer.java @@ -1,4 +1,4 @@ -package net.woggioni.worth.serialization.json; +package net.woggioni.worth.utils; public class Chronometer { diff --git a/src/main/java/net/woggioni/worth/value/ObjectValue.java b/src/main/java/net/woggioni/worth/value/ObjectValue.java index bf497ec..b018987 100644 --- a/src/main/java/net/woggioni/worth/value/ObjectValue.java +++ b/src/main/java/net/woggioni/worth/value/ObjectValue.java @@ -199,7 +199,7 @@ class ListObjectValue implements ObjectValue { @Override public void put(String key, Value value2Put) { - value.add(new ObjectEntry(key, value2Put)); + value.add(new ObjectEntry<>(key, value2Put)); } diff --git a/src/main/java/net/woggioni/worth/xface/Value.java b/src/main/java/net/woggioni/worth/xface/Value.java index 3fb7364..c1d142d 100644 --- a/src/main/java/net/woggioni/worth/xface/Value.java +++ b/src/main/java/net/woggioni/worth/xface/Value.java @@ -97,10 +97,6 @@ public interface Value { public final ObjectValue.Implementation objectValueImplementation = ObjectValue.Implementation.valueOf( System.getProperty(ObjectValue.class.getName() + ".implementation", "TreeMap")); - @Builder.Default - public final boolean useReferences = Boolean.valueOf( - System.getProperty(Value.class.getName() + ".useReferences", "false")); - @Builder.Default public final int maxDepth = Integer.parseInt(System.getProperty(Value.class.getName() + ".maxDepth", "1048576"));