/* * Copyright 2014 the original author or authors. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.xd.tuple.serializer.kryo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.util.Random; import org.junit.Before; import org.junit.Test; import org.springframework.integration.codec.kryo.PojoCodec; import org.springframework.util.StopWatch; import org.springframework.xd.tuple.DefaultTuple; import org.springframework.xd.tuple.Tuple; import org.springframework.xd.tuple.TupleBuilder; /** * @author David Turanski * @author Gary Russell */ public class TupleCodecBenchmarkTests { private static final int ITERATIONS = 50000; private static final int NUM_FIELDS = 10; private PojoCodec serializer; private PojoCodec deserializer; private Random random = new Random(System.currentTimeMillis()); private Tuple[] generateSamples(boolean includeNestedObjects) { Tuple[] tuples = new Tuple[ITERATIONS]; for (int i = 0; i < ITERATIONS; i++) { tuples[i] = randomTuple(NUM_FIELDS, includeNestedObjects); } return tuples; } @Before public void setUp() { serializer = new PojoCodec(new TupleKryoRegistrar()); deserializer = new PojoCodec(new TupleKryoRegistrar()); } @Test public void runBenchmarks() throws IOException { StopWatch stopWatch = new StopWatch("Tuple ser/deser - Iterations:" + ITERATIONS + " number of fields:" + NUM_FIELDS); Tuple[] primitiveTuples = generateSamples(false); Tuple[] nestedTuples = generateSamples(true); //warm up long endTime = System.currentTimeMillis() + 5000; int i = 0; do { runBenchmark(stopWatch, "warmup " + i, primitiveTuples, serializer, deserializer); i++; } while (System.currentTimeMillis() < endTime); runBenchmark(stopWatch, "primitives", primitiveTuples, serializer, deserializer); runBenchmark(stopWatch, "nested", nestedTuples, serializer, deserializer); System.out.println(stopWatch.prettyPrint()); for (StopWatch.TaskInfo taskInfo : stopWatch.getTaskInfo()) { if (taskInfo.getTaskName().equals("primitives") || taskInfo.getTaskName().equals("nested")) { double nanosecs = taskInfo.getTimeMillis() * 1000000.0; double averagens = nanosecs / ITERATIONS; System.out.println(taskInfo.getTaskName() + ": avg time (ns) " + averagens); } } } private void runBenchmark(StopWatch stopWatch, String taskName, Tuple[] tuples, PojoCodec serializer, PojoCodec deserializer) throws IOException { stopWatch.start(taskName); for (int i = 0; i < ITERATIONS; i++) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); serializer.encode(tuples[i], bos); byte[] bytes = bos.toByteArray(); Tuple result = deserializer.decode(bytes, DefaultTuple.class); assertEquals(tuples[i].getFieldNames(), result.getFieldNames()); assertEquals(tuples[i].getValues(), result.getValues()); assertNotNull(((DefaultTuple) tuples[i]).getConversionService()); } stopWatch.stop(); } //TODO: primitive arrays //TODO: Other types supported by Tuple, e.g. BigInteger //TODO: Can this be simplified with autoboxing? private Tuple randomTuple(int numFields, boolean includeNestedObjects) { Class<?>[] types = new Class<?>[] {int.class, boolean.class, char.class, long.class, float.class, double.class, byte.class, Byte.class, Integer.class, Boolean.class, Long.class, Double.class, String.class, Foo.class, Tuple.class}; int range = includeNestedObjects ? types.length : types.length - 2; TupleBuilder tupleBuilder = new TupleBuilder(); for (int i = 0; i < numFields; i++) { String name = "field" + i; Class<?> type = types[random.nextInt(range)]; Object value = null; if (type.equals(int.class)) { value = random.nextInt(); } else if (type.equals(Integer.class)) { value = new Integer(random.nextInt()); } else if (type.equals(boolean.class)) { value = random.nextBoolean(); } else if (type.equals(Boolean.class)) { value = new Boolean(random.nextBoolean()); } else if (type.equals(char.class)) { value = 'q'; } else if (type.equals(long.class)) { value = Math.abs(random.nextLong()); } else if (type.equals(Long.class)) { value = Math.abs(new Long(random.nextLong())); } else if (type.equals(byte.class)) { value = (byte) random.nextInt(127); } else if (type.equals(Byte.class)) { value = new Byte((byte) random.nextInt(127)); } else if (type.equals(float.class)) { value = random.nextFloat(); } else if (type.equals(Float.class)) { value = new Float(random.nextFloat()); } else if (type.equals(double.class)) { value = random.nextDouble(); } else if (type.equals(Double.class)) { value = new Double(random.nextDouble()); } else if (type.equals(String.class)) { value = new BigInteger(130, random).toString(32); } else if (type.equals(Foo.class)) { value = new Foo(); } else if (type.equals(Tuple.class)) { value = randomTuple(5, false); } tupleBuilder.put(name, value); } return tupleBuilder.build(); } static class Foo { private String s = ""; private boolean b; private int i; private long l; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Foo foo = (Foo) o; if (b != foo.b) return false; if (i != foo.i) return false; if (l != foo.l) return false; if (!s.equals(foo.s)) return false; return true; } @Override public int hashCode() { int result = s.hashCode(); result = 31 * result + (b ? 1 : 0); result = 31 * result + i; result = 31 * result + (int) (l ^ (l >>> 32)); return result; } public String getS() { return s; } public void setS(String s) { this.s = s; } public boolean isB() { return b; } public void setB(boolean b) { this.b = b; } public int getI() { return i; } public void setI(int i) { this.i = i; } public long getL() { return l; } public void setL(long l) { this.l = l; } } }