Skip to content

Commit daefba9

Browse files
committed
Replace hand-rolled benchmarks with JMH for msgpack-jackson3
The previous Benchmarker/System.nanoTime() approach had no forking, no dead-code elimination (Blackhole), and no JIT-aware warmup, producing ~25ms stdev results. Replace with proper JMH benchmarks modelled on https://github.com/FasterXML/jackson-benchmarks. Changes: - Add sbt-jmh 0.4.7 plugin; enable JmhPlugin on msgpack-jackson3 - Add --add-opens JVM args for forked JMH processes (required for msgpack-core's Unsafe usage) - Remove commons-math3 test dependency (no longer needed) - Delete Benchmarker, MessagePackDataformatPojoBenchmarkTest, MessagePackDataformatHugeDataBenchmarkTest - Add src/jmh/java benchmark sources: * model/: MediaItem, MediaContent, Image, Size, Player, MediaItems (canonical jvm-serializers dataset, same as jackson-benchmarks) * BenchmarkState: pre-serialized bytes + ObjectMapper instances * MsgpackReadBenchmark: readPojoMsgpack / readPojoJson * MsgpackWriteBenchmark: writePojoMsgpack / writePojoJson * NopOutputStream: /dev/null sink for write benchmarks Run: ./sbt "msgpack-jackson3/jmh:run" Smoke-test: ./sbt 'msgpack-jackson3/jmh:run -f 1 -wi 1 -i 1'
1 parent 1605bf4 commit daefba9

15 files changed

Lines changed: 276 additions & 396 deletions

File tree

build.sbt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ lazy val msgpackJackson = Project(id = "msgpack-jackson", base = file("msgpack-j
181181
.dependsOn(msgpackCore)
182182

183183
lazy val msgpackJackson3 = Project(id = "msgpack-jackson3", base = file("msgpack-jackson3"))
184-
.enablePlugins(SbtOsgi)
184+
.enablePlugins(SbtOsgi, JmhPlugin)
185185
.settings(
186186
buildSettings,
187187
name := "jackson-dataformat-msgpack3",
@@ -194,10 +194,13 @@ lazy val msgpackJackson3 = Project(id = "msgpack-jackson3", base = file("msgpack
194194
doc / javacOptions := Seq("-source", "17", "-Xdoclint:none"),
195195
libraryDependencies ++=
196196
Seq(
197-
"tools.jackson.core" % "jackson-databind" % "3.1.2",
198-
junitInterface,
199-
"org.apache.commons" % "commons-math3" % "3.6.1" % "test"
197+
"tools.jackson.core" % "jackson-databind" % "3.1.2",
198+
junitInterface
200199
),
201-
testOptions += Tests.Argument(TestFrameworks.JUnit, "-v")
200+
testOptions += Tests.Argument(TestFrameworks.JUnit, "-v"),
201+
Jmh / javaOptions ++= Seq(
202+
"--add-opens=java.base/java.nio=ALL-UNNAMED",
203+
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED"
204+
)
202205
)
203206
.dependsOn(msgpackCore)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.msgpack.jackson.dataformat.benchmark;
2+
3+
import org.msgpack.jackson.dataformat.MessagePackFactory;
4+
import org.msgpack.jackson.dataformat.MessagePackMapper;
5+
import org.msgpack.jackson.dataformat.benchmark.model.MediaItem;
6+
import org.msgpack.jackson.dataformat.benchmark.model.MediaItems;
7+
import org.openjdk.jmh.annotations.Scope;
8+
import org.openjdk.jmh.annotations.State;
9+
import tools.jackson.databind.ObjectMapper;
10+
import tools.jackson.databind.json.JsonMapper;
11+
12+
@State(Scope.Thread)
13+
public class BenchmarkState
14+
{
15+
public final ObjectMapper msgpackMapper = MessagePackMapper.builder(new MessagePackFactory()).build();
16+
public final ObjectMapper jsonMapper = JsonMapper.builder().build();
17+
18+
public final byte[] msgpackBytes;
19+
public final byte[] jsonBytes;
20+
21+
public BenchmarkState()
22+
{
23+
try {
24+
MediaItem item = MediaItems.stdMediaItem();
25+
msgpackBytes = msgpackMapper.writeValueAsBytes(item);
26+
jsonBytes = jsonMapper.writeValueAsBytes(item);
27+
}
28+
catch (Exception e) {
29+
throw new RuntimeException(e);
30+
}
31+
}
32+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.msgpack.jackson.dataformat.benchmark;
2+
3+
import org.msgpack.jackson.dataformat.benchmark.model.MediaItem;
4+
import org.openjdk.jmh.annotations.Benchmark;
5+
import org.openjdk.jmh.annotations.BenchmarkMode;
6+
import org.openjdk.jmh.annotations.Fork;
7+
import org.openjdk.jmh.annotations.Measurement;
8+
import org.openjdk.jmh.annotations.Mode;
9+
import org.openjdk.jmh.annotations.OutputTimeUnit;
10+
import org.openjdk.jmh.annotations.Scope;
11+
import org.openjdk.jmh.annotations.State;
12+
import org.openjdk.jmh.annotations.Warmup;
13+
14+
import java.util.concurrent.TimeUnit;
15+
16+
@BenchmarkMode(Mode.Throughput)
17+
@OutputTimeUnit(TimeUnit.SECONDS)
18+
@State(Scope.Thread)
19+
@Fork(value = 2, jvmArgsAppend = {
20+
"--add-opens=java.base/java.nio=ALL-UNNAMED",
21+
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED"
22+
})
23+
@Warmup(iterations = 5, time = 1)
24+
@Measurement(iterations = 5, time = 1)
25+
public class MsgpackReadBenchmark
26+
{
27+
private final BenchmarkState state = new BenchmarkState();
28+
29+
@Benchmark
30+
public Object readPojoMsgpack() throws Exception
31+
{
32+
return state.msgpackMapper.readValue(state.msgpackBytes, MediaItem.class);
33+
}
34+
35+
@Benchmark
36+
public Object readPojoJson() throws Exception
37+
{
38+
return state.jsonMapper.readValue(state.jsonBytes, MediaItem.class);
39+
}
40+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.msgpack.jackson.dataformat.benchmark;
2+
3+
import org.msgpack.jackson.dataformat.benchmark.model.MediaItem;
4+
import org.msgpack.jackson.dataformat.benchmark.model.MediaItems;
5+
import org.openjdk.jmh.annotations.Benchmark;
6+
import org.openjdk.jmh.annotations.BenchmarkMode;
7+
import org.openjdk.jmh.annotations.Fork;
8+
import org.openjdk.jmh.annotations.Measurement;
9+
import org.openjdk.jmh.annotations.Mode;
10+
import org.openjdk.jmh.annotations.OutputTimeUnit;
11+
import org.openjdk.jmh.annotations.Scope;
12+
import org.openjdk.jmh.annotations.State;
13+
import org.openjdk.jmh.annotations.Warmup;
14+
15+
import java.util.concurrent.TimeUnit;
16+
17+
@BenchmarkMode(Mode.Throughput)
18+
@OutputTimeUnit(TimeUnit.SECONDS)
19+
@State(Scope.Thread)
20+
@Fork(value = 2, jvmArgsAppend = {
21+
"--add-opens=java.base/java.nio=ALL-UNNAMED",
22+
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED"
23+
})
24+
@Warmup(iterations = 5, time = 1)
25+
@Measurement(iterations = 5, time = 1)
26+
public class MsgpackWriteBenchmark
27+
{
28+
private final BenchmarkState state = new BenchmarkState();
29+
private final MediaItem item = MediaItems.stdMediaItem();
30+
31+
@Benchmark
32+
public int writePojoMsgpack() throws Exception
33+
{
34+
NopOutputStream out = new NopOutputStream();
35+
state.msgpackMapper.writeValue(out, item);
36+
return out.size();
37+
}
38+
39+
@Benchmark
40+
public int writePojoJson() throws Exception
41+
{
42+
NopOutputStream out = new NopOutputStream();
43+
state.jsonMapper.writeValue(out, item);
44+
return out.size();
45+
}
46+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.msgpack.jackson.dataformat.benchmark;
2+
3+
import java.io.OutputStream;
4+
5+
public class NopOutputStream
6+
extends OutputStream
7+
{
8+
private int size;
9+
10+
@Override
11+
public void write(int b)
12+
{
13+
size++;
14+
}
15+
16+
@Override
17+
public void write(byte[] b, int off, int len)
18+
{
19+
size += len;
20+
}
21+
22+
public int size()
23+
{
24+
return size;
25+
}
26+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.msgpack.jackson.dataformat.benchmark.model;
2+
3+
public class Image
4+
{
5+
public String uri;
6+
public String title;
7+
public int width;
8+
public int height;
9+
public Size size;
10+
11+
public Image() {}
12+
13+
public Image(String uri, String title, int width, int height, Size size)
14+
{
15+
this.uri = uri;
16+
this.title = title;
17+
this.width = width;
18+
this.height = height;
19+
this.size = size;
20+
}
21+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.msgpack.jackson.dataformat.benchmark.model;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
public class MediaContent
7+
{
8+
public String uri;
9+
public String title;
10+
public int width;
11+
public int height;
12+
public String format;
13+
public long duration;
14+
public long size;
15+
public int bitrate;
16+
public List<String> persons;
17+
public Player player;
18+
public String copyright;
19+
20+
public MediaContent() {}
21+
22+
public void addPerson(String person)
23+
{
24+
if (persons == null) {
25+
persons = new ArrayList<>();
26+
}
27+
persons.add(person);
28+
}
29+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.msgpack.jackson.dataformat.benchmark.model;
2+
3+
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
4+
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
8+
@JsonPropertyOrder({"content", "images"})
9+
public class MediaItem
10+
{
11+
public MediaContent content;
12+
public List<Image> images;
13+
14+
public MediaItem() {}
15+
16+
public MediaItem(MediaContent content)
17+
{
18+
this.content = content;
19+
}
20+
21+
public void addImage(Image image)
22+
{
23+
if (images == null) {
24+
images = new ArrayList<>();
25+
}
26+
images.add(image);
27+
}
28+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.msgpack.jackson.dataformat.benchmark.model;
2+
3+
public class MediaItems
4+
{
5+
private static final MediaItem STD_MEDIA_ITEM;
6+
7+
static {
8+
MediaContent content = new MediaContent();
9+
content.uri = "http://javaone.com/keynote.mpg";
10+
content.title = "Javaone Keynote";
11+
content.width = 640;
12+
content.height = 480;
13+
content.format = "video/mpg4";
14+
content.duration = 18000000L;
15+
content.size = 58982400L;
16+
content.bitrate = 262144;
17+
content.player = Player.JAVA;
18+
content.copyright = "None";
19+
content.addPerson("Bill Gates");
20+
content.addPerson("Steve Jobs");
21+
22+
MediaItem item = new MediaItem(content);
23+
item.addImage(new Image("http://javaone.com/keynote_large.jpg", "Javaone Keynote", 1024, 768, Size.LARGE));
24+
item.addImage(new Image("http://javaone.com/keynote_small.jpg", "Javaone Keynote", 320, 240, Size.SMALL));
25+
26+
STD_MEDIA_ITEM = item;
27+
}
28+
29+
public static MediaItem stdMediaItem()
30+
{
31+
return STD_MEDIA_ITEM;
32+
}
33+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.msgpack.jackson.dataformat.benchmark.model;
2+
3+
public enum Player
4+
{
5+
JAVA, FLASH
6+
}

0 commit comments

Comments
 (0)