为什么 Jackson Streaming API 很慢?

Ser*_*rge 5 java jackson jackson-modules jmh java-11

根据 Jackson Streaming API 文档:

如果您明确重写所有转换以使用 Streaming API 而不是数据绑定,您可能能够将吞吐量提高 30-40%;这对实际生成的 JSON 没有任何更改。

阅读全文:https : //github.com/FasterXML/jackson-docs/wiki/Presentation : -Jackson-Performance

但是,根据我的基准测试,在最佳情况下吞吐量提高了 15-20%,对于写入复杂对象,甚至降低了约 23%。

难道我做错了什么?为什么使用流式 api 的复杂对象的写入操作比使用在流式 api 之上工作的 ObjectMapper 慢?

非常有趣的是,对于 java11/12,Jackson Streaming 的写入性能下降更为显着(约 2% 相比于 Java 11/12 的约 24%)。

配置:

  • 8个CPU
  • 64 GB 内存
  • 操作系统版本 16.04.1 LTS (Xenial Xerus)
  • Docker 版本 18.03.0-ce,构建 0520e24

JMH 吞吐量结果:

  • 预热:10 次迭代,每次 10 秒
  • 测量:10 次迭代,每次 10 秒
  • 线程:1 个线程,将同步迭代
  • 虚拟机选项:-XX:+UseG1GC -server -Xmx4096m -Xms4096m
  • 单位:操作数/秒

复杂对象

大对象 json 对象大小 ~ 52KB

普通对象

小物体 json 对象大小 ~ 72B

结构:

// Plain object 
public class Sport{
    Long id;
    String name;
}

// Complex object 
public class Event {
    Long id;
    Long sportId;
    String name;
    Date startTime;
    boolean inRunning;
    boolean allowLiveBetting;
    List<Market> markets;
    List<EventParticipant> eventParticipants;
}

public class Market {
    Long id;
    Long eventId;
    String name;
    boolean inRunning;
    boolean allowLiveBetting;
    Double value;
    Integer winners;
    List<Runner> runners;
}

public class Runner {
    Long id;
    Long eventId;
    Long marketId;
    Long eventParticipantId;
    String name;
    boolean withdrawn;
    Double value;
}

public class EventParticipant{
    Long id;
    Long eventId;
    String participantName;
    String jockeyName;
    String trainerName;
    Integer number;
}
Run Code Online (Sandbox Code Playgroud)

代码

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    public void complexJacksonStreamWrite(ExecutionPlan plan, Blackhole blackhole) throws IOException {
        Event event = plan.getBigObjects();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try (JsonGenerator generator =
                plan.getFactory().createGenerator(
                        outputStream, JsonEncoding.UTF8)) {
            generator.writeStartObject();
            generator.writeNumberField("id", event.getId());
            // populate rest attributes
            generator.writeFieldName("eventParticipants");
            generator.writeStartArray();
            for (int i = 0; i < event.getEventParticipants().size(); i++) {
                EventParticipant eventParticipant = event.getEventParticipants().get(i);
                generator.writeStartObject();
                generator.writeNumberField("id", eventParticipant.getId());
                // populate rest attributes 
                generator.writeEndObject();
            }
            generator.writeEndArray();
            generator.writeEndObject();
        }
        blackhole.consume(outputStream);
    }
Run Code Online (Sandbox Code Playgroud)

怎么跑

爪哇 8

docker run -it volkodav/java-jackson-benchmark:java8

爪哇 11

docker run -it volkodav/java-jackson-benchmark:java11

爪哇 12

docker run -it volkodav/java-jackson-benchmark:java12

源代码:

https://github.com/volkodavs/jackson-benchmark


更新

在评论@Jom Vernee 之后,他建议设置 ByteArrayOutputStream 的大小 - Jackson Streaming Writes 的性能提高了约 10%。

ByteArrayOutputStream outputStream = new ByteArrayOutputStream(60_000);
Run Code Online (Sandbox Code Playgroud)

复杂的

复杂的

清楚的

清楚的