package perf; import java.io.*; import java.util.*; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.*; /** * Variant that uses hard-coded input but compares cost of generic type * resolution to that of passing raw (type-erased) Class. * * @since 2.8 */ public class ManualReadWithTypeResolution { private final String _desc1, _desc2; private final byte[] _input; private final Class<?> _inputType; private final TypeReference<?> _inputTypeRef; private final ObjectMapper _mapper; private final int REPS; protected int hash; // wait for 3 seconds protected long startMeasure = System.currentTimeMillis() + 3000L; protected int roundsDone = 0; private double[] timeMsecs; public static void main(String[] args) throws Exception { new ManualReadWithTypeResolution().doTest(); } private ManualReadWithTypeResolution() throws IOException { _desc1 = "Raw type"; _desc2 = "Generic type"; _mapper = new ObjectMapper(); _input = "[\"value\",\"123\"]".getBytes("UTF-8"); _inputType = List.class; _inputTypeRef = new TypeReference<List<String>>() { }; /* _input = "{\"id\":124}".getBytes("UTF-8"); _inputType = Map.class; _inputTypeRef = new TypeReference<Map<String,Object>>() { }; */ REPS = (int) ((double) (15 * 1000 * 1000) / (double) _input.length); } // When comparing to simple streaming parsing, uncomment: private void doTest() throws Exception { System.out.printf("Read %d bytes to bind; will do %d repetitions\n", _input.length, REPS); System.out.print("Warming up"); int i = 0; final int TYPES = 2; timeMsecs = new double[TYPES]; while (true) { Thread.sleep(100L); final int type = (i++ % TYPES); String msg; double msesc; switch (type) { case 0: msesc = testDeser(REPS, _input, _mapper, _inputType); msg = _desc1; break; case 1: msesc = testDeser(REPS, _input, _mapper, _inputTypeRef); msg = _desc2; break; default: throw new Error(); } updateStats(type, (i % 17) == 0, msg, msesc); } } protected final double testDeser(int reps, byte[] json, ObjectMapper mapper, Class<?> type) throws IOException { long start = System.nanoTime(); Object result = null; while (--reps >= 0) { result = mapper.readValue(json, type); } hash = result.hashCode(); return _msecsFromNanos(System.nanoTime() - start); } protected final double testDeser(int reps, byte[] json, ObjectMapper mapper, TypeReference<?> type) throws IOException { long start = System.nanoTime(); Object result = null; while (--reps >= 0) { result = mapper.readValue(json, type); } hash = result.hashCode(); return _msecsFromNanos(System.nanoTime() - start); } private void updateStats(int type, boolean doGc, String msg, double msecs) throws Exception { final boolean lf = (type == (timeMsecs.length - 1)); if (startMeasure == 0L) { // skip first N seconds timeMsecs[type] += msecs; } else { if (lf) { if (System.currentTimeMillis() >= startMeasure) { startMeasure = 0L; System.out.println(" complete!"); } else { System.out.print("."); } } return; } System.out.printf("Test '%s' [hash: 0x%s] -> %.1f msecs\n", msg, Integer.toHexString(hash), msecs); if (lf) { ++roundsDone; if ((roundsDone % 3) == 0 ) { double den = (double) roundsDone; System.out.printf("Averages after %d rounds (%s/%s): %.1f / %.1f msecs\n", (int) den, _desc1, _desc2, timeMsecs[0] / den, timeMsecs[1] / den); } System.out.println(); } if (doGc) { System.out.println("[GC]"); Thread.sleep(100L); System.gc(); Thread.sleep(100L); } } protected final double _msecsFromNanos(long nanos) { return (nanos / 1000000.0); } }