package com.ankurm.jackson3.beyond; import tools.jackson.core.json.JsonFactory; import tools.jackson.core.util.JsonRecyclerPools; import tools.jackson.core.util.RecyclerPool; import tools.jackson.databind.json.JsonMapper; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * BEYOND THE POSTS — the performance knob the comparison post names but never measures. * * Jackson 3 changed the default buffer RecyclerPool. The post says to restore the 2.x * thread-local pool if you see a regression. Whether that helps depends entirely on your * concurrency profile, so this measures it on the machine you are actually running on * instead of asserting a winner. * * Indicative timings, not JMH. Run it a few times; the numbers move. */ public class Y06RecyclerPoolTuning { public record Payload(long id, String name, List tags, double amount) { } private static final int ITERATIONS = 40_000; public static void main(String[] args) throws Exception { System.out.println("default pool : " + JsonRecyclerPools.defaultPool().getClass().getSimpleName()); System.out.println("cores : " + Runtime.getRuntime().availableProcessors()); System.out.println(); for (int threads : new int[] { 1, 8 }) { System.out.println("--- " + threads + " thread(s), " + ITERATIONS + " round-trips each ---"); run(threads, "threadLocalPool (2.x default)", JsonRecyclerPools.threadLocalPool()); run(threads, "concurrentDeque (3.x default)", JsonRecyclerPools.newConcurrentDequePool()); run(threads, "nonRecyclingPool (no reuse) ", JsonRecyclerPools.nonRecyclingPool()); System.out.println(); } } private static void run(int threads, String label, RecyclerPool pool) throws Exception { @SuppressWarnings({ "unchecked", "rawtypes" }) JsonFactory factory = JsonFactory.builder().recyclerPool((RecyclerPool) pool).build(); JsonMapper mapper = JsonMapper.builder(factory).build(); Payload sample = new Payload(1L, "example", List.of("a", "b", "c"), 12.5); for (int i = 0; i < 2_000; i++) { // warm up mapper.readValue(mapper.writeValueAsString(sample), Payload.class); } ExecutorService pooled = Executors.newFixedThreadPool(threads); long start = System.nanoTime(); var tasks = java.util.stream.IntStream.range(0, threads) .>mapToObj(t -> () -> { for (int i = 0; i < ITERATIONS; i++) { mapper.readValue(mapper.writeValueAsString(sample), Payload.class); } return null; }).toList(); for (var future : pooled.invokeAll(tasks)) future.get(); long ms = (System.nanoTime() - start) / 1_000_000; pooled.shutdown(); System.out.printf(" %-34s %5d ms%n", label, ms); } }