| 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(); |
| } |
| } |