package perf; import java.io.*; import com.fasterxml.jackson.databind.*; abstract class ObjectReaderTestBase { protected final static int WARMUP_ROUNDS = 5; protected String _desc1, _desc2; protected int hash; protected long startMeasure = System.currentTimeMillis() + 5000L; protected int roundsDone = 0; protected int REPS; private double[] timeMsecs; protected abstract int targetSizeMegs(); protected void testFromBytes(ObjectMapper mapper1, String desc1, Object inputValue1, Class<?> inputClass1, ObjectMapper mapper2, String desc2, Object inputValue2, Class<?> inputClass2) throws Exception { final byte[] byteInput1 = mapper1.writeValueAsBytes(inputValue1); final byte[] byteInput2 = mapper2.writeValueAsBytes(inputValue2); // Let's try to guestimate suitable size... to get to N megs to process REPS = (int) ((double) (targetSizeMegs() * 1000 * 1000) / (double) byteInput1.length); // sanity check: /*T1 back1 =*/ mapper1.readValue(byteInput1, inputClass1); /*T2 back2 =*/ mapper2.readValue(byteInput2, inputClass2); System.out.println("Input successfully round-tripped for both styles..."); _desc1 = String.format("%s (%d bytes)", desc1, byteInput1.length); _desc2 = String.format("%s (%d bytes)", desc2, byteInput2.length); doTest(mapper1, byteInput1, inputClass1, mapper2, byteInput2, inputClass2); } protected void testFromString(ObjectMapper mapper1, String desc1, Object inputValue1, Class<?> inputClass1, ObjectMapper mapper2, String desc2, Object inputValue2, Class<?> inputClass2) throws Exception { final String input1 = mapper1.writeValueAsString(inputValue1); final String input2 = mapper2.writeValueAsString(inputValue2); // Let's try to guestimate suitable size... to get to N megs to process REPS = (int) ((double) (targetSizeMegs() * 1000 * 1000) / (double) input1.length()); _desc1 = String.format("%s (%d chars)", desc1, input1.length()); _desc2 = String.format("%s (%d chars)", desc2, input2.length()); // sanity check: /*T1 back1 =*/ mapper1.readValue(input1, inputClass1); /*T2 back2 =*/ mapper2.readValue(input2, inputClass2); System.out.println("Input successfully round-tripped for both styles..."); doTest(mapper1, input1, inputClass1, mapper2, input2, inputClass2); } protected void doTest(ObjectMapper mapper1, byte[] byteInput1, Class<?> inputClass1, ObjectMapper mapper2, byte[] byteInput2, Class<?> inputClass2) throws Exception { System.out.printf("Read %d bytes to bind (%d as array); will do %d repetitions\n", byteInput1.length, byteInput2.length, REPS); System.out.print("Warming up"); final ObjectReader jsonReader = mapper1.reader() .forType(inputClass1); final ObjectReader arrayReader = mapper2.reader() .forType(inputClass2); 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: msg = _desc1; msesc = testDeser1(REPS, byteInput1, jsonReader); break; case 1: msg = _desc2; msesc = testDeser2(REPS, byteInput2, arrayReader); break; default: throw new Error(); } updateStats(type, (i % 17) == 0, msg, msesc); } } protected void doTest(ObjectMapper mapper1, String input1, Class<?> inputClass1, ObjectMapper mapper2, String input2, Class<?> inputClass2) throws Exception { System.out.printf("Read %d bytes to bind (%d as array); will do %d repetitions\n", input1.length(), input2.length(), REPS); System.out.print("Warming up"); final ObjectReader jsonReader = mapper1.reader() .with(DeserializationFeature.EAGER_DESERIALIZER_FETCH) .forType(inputClass1); final ObjectReader arrayReader = mapper2.reader() .with(DeserializationFeature.EAGER_DESERIALIZER_FETCH) .forType(inputClass2); int i = 0; final int TYPES = 2; timeMsecs = new double[TYPES]; while (true) { Thread.sleep(100L); int type = (i++ % TYPES); String msg; double msecs; switch (type) { case 0: msg = _desc1; msecs = testDeser1(REPS, input1, jsonReader); break; case 1: msg = _desc2; msecs = testDeser2(REPS, input2, arrayReader); break; default: throw new Error(); } updateStats(type, (i % 17) == 0, msg, msecs); } } 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 double testDeser1(int reps, byte[] input, ObjectReader reader) throws Exception { return _testDeser(reps, input, reader); } protected double testDeser2(int reps, byte[] input, ObjectReader reader) throws Exception { return _testDeser(reps, input, reader); } protected final double _testDeser(int reps, byte[] input, ObjectReader reader) throws Exception { long start = System.nanoTime(); Object result = null; while (--reps >= 0) { result = reader.readValue(input); } hash = result.hashCode(); // return microseconds return _msecsFromNanos(System.nanoTime() - start); } protected double testDeser1(int reps, String input, ObjectReader reader) throws Exception { return _testDeser(reps, input, reader); } protected double testDeser2(int reps, String input, ObjectReader reader) throws Exception { return _testDeser(reps, input, reader); } protected final double _testDeser(int reps, String input, ObjectReader reader) throws Exception { long start = System.nanoTime(); Object result = null; while (--reps >= 0) { result = reader.readValue(input); } hash = result.hashCode(); return _msecsFromNanos(System.nanoTime() - start); } protected final double _msecsFromNanos(long nanos) { return (nanos / 1000000.0); } protected static byte[] readAll(String filename) throws IOException { File f = new File(filename); ByteArrayOutputStream bytes = new ByteArrayOutputStream((int) f.length()); byte[] buffer = new byte[4000]; int count; FileInputStream in = new FileInputStream(f); while ((count = in.read(buffer)) > 0) { bytes.write(buffer, 0, count); } in.close(); return bytes.toByteArray(); } }