blob: 42d6a6a910cfbeedcc96c7a0a7c1e1eb19213673 [file] [log] [blame]
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -07001package net.onrc.onos.core.util.serializers;
2
3import static org.junit.Assert.*;
4
5import java.io.PrintStream;
6import java.lang.reflect.Constructor;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.List;
10
11import net.floodlightcontroller.util.MACAddress;
12import net.onrc.onos.core.topology.HostEvent;
13import net.onrc.onos.core.topology.LinkEvent;
14import net.onrc.onos.core.topology.PortEvent;
15import net.onrc.onos.core.topology.TopologyElement;
16import net.onrc.onos.core.topology.TopologyEvent;
17import net.onrc.onos.core.topology.SwitchEvent;
18import net.onrc.onos.core.util.Dpid;
19import net.onrc.onos.core.util.PortNumber;
20import net.onrc.onos.core.util.SwitchPort;
21
22import org.junit.Before;
23import org.junit.Test;
24
25import com.esotericsoftware.kryo.Kryo;
26import com.esotericsoftware.kryo.io.Input;
27import com.esotericsoftware.kryo.io.Output;
28
29/**
30 * Tests to capture Kryo serialization characteristics.
31 * <p/>
32 * <ul>
33 * <li>Serialization/deserialization throughput</li>
34 * <li>Serialized size</li>
35 * <li>Equality of object before and after serializaton,deserialization</li>
36 * <li>TODO bit by bit comparison of serialized bytes</li>
37 * </ul>
38 */
39public class KryoFactoryTest {
40
41 private static final int NUM_ITERATIONS = Integer.parseInt(
42 System.getProperty("iterations", "100"));
43
44 private static final Dpid DPID_A = new Dpid(0x1234L);
45 private static final Dpid DPID_B = new Dpid(Long.MAX_VALUE);
46 private static final PortNumber PORT_NO_A = new PortNumber((short) 42);
47 private static final PortNumber PORT_NO_B = new PortNumber((short) 65534);
48
49 private static final double SEC_IN_NANO = 1000 * 1000 * 1000.0;
50
51 private KryoFactory kryoFactory;
52
53 @Before
54 public void setUp() throws Exception {
55 kryoFactory = new KryoFactory(1);
56 }
57
58 /**
59 * Benchmark result.
60 */
61 private static final class Result {
62 /**
63 * Serialized type name.
64 */
65 String type;
66 /**
67 * Serialized size.
68 */
69 int size;
70 /**
71 * Serialization throughput (ops/sec).
72 */
73 double ser;
74 /**
75 * Deserialization throughput (ops/sec).
76 */
77 double deser;
78
79 public Result(String type, int size, double ser, double deser) {
80 this.type = type;
81 this.size = size;
82 this.ser = ser;
83 this.deser = deser;
84 }
85 }
86
87 private static enum EqualityCheck {
88 /**
89 * No way to compare equality provided.
90 */
91 IMPOSSIBLE,
92 /**
93 * custom equals method is defined.
94 */
95 EQUALS,
96 /**
97 * Can be compared using toString() result.
98 */
99 TO_STRING,
100 }
101
102 /**
103 * Benchmark serialization of specified object.
104 *
105 * @param obj the object to benchmark
106 * @param equalityCheck how to check equality of deserialized object.
107 * @return benchmark {@link Result}
108 */
109 private Result benchType(Object obj, EqualityCheck equalityCheck) {
110
111 Kryo kryo = kryoFactory.newKryo();
112 try {
113 byte[] buffer = new byte[1 * 1000 * 1000];
114 Output output = new Output(buffer);
115
116 // Measurement: serialization size
117 kryo.writeClassAndObject(output, obj);
118 int serializedBytes = output.toBytes().length;
119
120 // Measurement: serialization throughput
121 byte[] result = null;
122
123 long t1 = System.nanoTime();
124 for (int j = 0; j < NUM_ITERATIONS; j++) {
125 output.clear();
126 kryo.writeClassAndObject(output, obj);
127 result = output.toBytes();
128 }
129 long t2 = System.nanoTime();
130 double serTput = NUM_ITERATIONS * SEC_IN_NANO / (t2 - t1);
131
132 // Measurement: deserialization throughput
133 Object objOut = null;
134 Input input = new Input(result);
135 long t3 = System.nanoTime();
136 for (int j = 0; j < NUM_ITERATIONS; j++) {
137 input.setBuffer(result);
138 objOut = kryo.readClassAndObject(input);
139 }
140 long t4 = System.nanoTime();
141
142 switch (equalityCheck) {
143 case IMPOSSIBLE:
144 break;
145 case EQUALS:
146 assertEquals(obj, objOut);
147 break;
148 case TO_STRING:
149 assertEquals(obj.toString(), objOut.toString());
150 break;
151 default:
152 break;
153 }
154 double deserTput = NUM_ITERATIONS * SEC_IN_NANO / (t4 - t3);
155
156 return new Result(obj.getClass().getSimpleName(),
157 serializedBytes, serTput, deserTput);
158 } finally {
159 kryoFactory.deleteKryo(kryo);
160 }
161 }
162
163 /**
164 * Benchmark serialization of types registered to KryoFactory.
165 */
166 @Test
167 public void benchmark() throws Exception {
168
169 List<Result> results = new ArrayList<>();
170
171 // To be more strict, we should be checking serialized byte[].
172 { // CHECKSTYLE IGNORE THIS LINE
173 HostEvent obj = new HostEvent(MACAddress.valueOf(0x12345678));
174 obj.createStringAttribute(TopologyElement.TYPE, TopologyElement.TYPE_PACKET_LAYER);
175 obj.addAttachmentPoint(new SwitchPort(DPID_A, PORT_NO_A));
176 // avoid using System.currentTimeMillis() var-int size may change
177 obj.setLastSeenTime(392860800000L);
178 obj.freeze();
179 Result result = benchType(obj, EqualityCheck.EQUALS);
180 results.add(result);
181 // update me if serialized form is expected to change
182 assertEquals(43, result.size);
183 }
184
185 { // CHECKSTYLE IGNORE THIS LINE
186 LinkEvent obj = new LinkEvent(DPID_A, PORT_NO_A, DPID_B, PORT_NO_B);
187 obj.createStringAttribute(TopologyElement.TYPE, TopologyElement.TYPE_PACKET_LAYER);
188 obj.freeze();
189 Result result = benchType(obj, EqualityCheck.EQUALS);
190 results.add(result);
191 // update me if serialized form is expected to change
192 assertEquals(49, result.size);
193 }
194
195 { // CHECKSTYLE IGNORE THIS LINE
196 PortEvent obj = new PortEvent(DPID_A, PORT_NO_A);
197 obj.createStringAttribute(TopologyElement.TYPE, TopologyElement.TYPE_PACKET_LAYER);
198 obj.freeze();
199 Result result = benchType(obj, EqualityCheck.EQUALS);
200 results.add(result);
201 // update me if serialized form is expected to change
202 assertEquals(24, result.size);
203 }
204
205 { // CHECKSTYLE IGNORE THIS LINE
206 SwitchEvent obj = new SwitchEvent(DPID_A);
207 obj.createStringAttribute(TopologyElement.TYPE, TopologyElement.TYPE_PACKET_LAYER);
208 obj.freeze();
209 Result result = benchType(obj, EqualityCheck.EQUALS);
210 results.add(result);
211 // update me if serialized form is expected to change
212 assertEquals(21, result.size);
213 }
214
215 { // CHECKSTYLE IGNORE THIS LINE
216 SwitchEvent evt = new SwitchEvent(DPID_A);
217 evt.createStringAttribute(TopologyElement.TYPE, TopologyElement.TYPE_PACKET_LAYER);
218 evt.freeze();
219
220 // using the back door to access package-scoped constructor
221 Constructor<TopologyEvent> swConst
222 = TopologyEvent.class.getDeclaredConstructor(SwitchEvent.class);
223 swConst.setAccessible(true);
224 TopologyEvent obj = swConst.newInstance(evt);
225
226 Result result = benchType(obj, EqualityCheck.TO_STRING);
227 results.add(result);
228 // update me if serialized form is expected to change
229 assertEquals(26, result.size);
230 }
231
232 // TODO Add registered classes we still use.
233
234
235 // Output for plot plugin
236 List<String> slabels = new ArrayList<>();
237 List<Number> svalues = new ArrayList<>();
238 List<String> tlabels = new ArrayList<>();
239 List<Number> tvalues = new ArrayList<>();
240
241 // Type, size, serialize T-put, deserialize T-put, N
242 System.out.println("Type, size, serialize T-put, deserialize T-put, N");
243
244 for (Result result : results) {
245 System.out.printf("%s, %d, %f, %f, %d\n",
246 result.type, result.size, result.ser, result.deser,
247 NUM_ITERATIONS);
248
249 // Output for plot plugin
250 // <Type>_size, <Type>_ser, <Type>_deser
251 slabels.addAll(Arrays.asList(
252 result.type + "_size"
253 ));
254 svalues.addAll(Arrays.asList(
255 result.size
256 ));
257 tlabels.addAll(Arrays.asList(
258 result.type + "_ser",
259 result.type + "_deser"
260 ));
261 tvalues.addAll(Arrays.asList(
262 result.ser,
263 result.deser
264 ));
265 }
266
267 // Output for plot plugin
268 PrintStream size = new PrintStream("target/KryoFactoryTest_size.csv");
269 PrintStream tput = new PrintStream("target/KryoFactoryTest_tput.csv");
270
271 for (String label : slabels) {
272 size.print(label);
273 size.print(", ");
274 }
275 size.println();
276 for (Number value : svalues) {
277 size.print(value);
278 size.print(", ");
279 }
280 size.close();
281
282 for (String label : tlabels) {
283 tput.print(label);
284 tput.print(", ");
285 }
286 tput.println();
287 for (Number value : tvalues) {
288 tput.print(value);
289 tput.print(", ");
290 }
291 tput.close();
292 }
293}