blob: 732174d121ff014c5b4d54e45a93fae6129b0692 [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;
Yuta HIGUCHIe57e10e2014-08-20 14:25:30 -07009import java.util.HashSet;
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -070010import java.util.List;
Yuta HIGUCHIe57e10e2014-08-20 14:25:30 -070011import java.util.Set;
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -070012
13import net.floodlightcontroller.util.MACAddress;
14import net.onrc.onos.core.topology.HostEvent;
15import net.onrc.onos.core.topology.LinkEvent;
16import net.onrc.onos.core.topology.PortEvent;
Pavlin Radoslavovdd08e8c2014-08-14 11:02:57 -070017import net.onrc.onos.core.topology.TopologyBatchOperation;
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -070018import net.onrc.onos.core.topology.TopologyElement;
19import net.onrc.onos.core.topology.TopologyEvent;
20import net.onrc.onos.core.topology.SwitchEvent;
21import net.onrc.onos.core.util.Dpid;
Yuta HIGUCHI630216e2014-07-30 09:09:34 -070022import net.onrc.onos.core.util.OnosInstanceId;
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -070023import net.onrc.onos.core.util.PortNumber;
24import net.onrc.onos.core.util.SwitchPort;
25
26import org.junit.Before;
27import org.junit.Test;
28
29import com.esotericsoftware.kryo.Kryo;
30import com.esotericsoftware.kryo.io.Input;
31import com.esotericsoftware.kryo.io.Output;
Yuta HIGUCHIe57e10e2014-08-20 14:25:30 -070032import com.google.common.collect.Sets;
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -070033
34/**
35 * Tests to capture Kryo serialization characteristics.
36 * <p/>
37 * <ul>
38 * <li>Serialization/deserialization throughput</li>
39 * <li>Serialized size</li>
40 * <li>Equality of object before and after serializaton,deserialization</li>
41 * <li>TODO bit by bit comparison of serialized bytes</li>
42 * </ul>
43 */
44public class KryoFactoryTest {
45
46 private static final int NUM_ITERATIONS = Integer.parseInt(
47 System.getProperty("iterations", "100"));
48
49 private static final Dpid DPID_A = new Dpid(0x1234L);
50 private static final Dpid DPID_B = new Dpid(Long.MAX_VALUE);
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -070051 private static final PortNumber PORT_NO_A = PortNumber.uint16((short) 42);
52 private static final PortNumber PORT_NO_B = PortNumber.uint16((short) 65534);
Pavlin Radoslavova5637c02014-07-30 15:55:11 -070053 private static final String ONOS_INSTANCE_NAME = "ONOS-Instance-Test";
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -070054
55 private static final double SEC_IN_NANO = 1000 * 1000 * 1000.0;
56
57 private KryoFactory kryoFactory;
58
59 @Before
60 public void setUp() throws Exception {
61 kryoFactory = new KryoFactory(1);
62 }
63
64 /**
Yuta HIGUCHIe57e10e2014-08-20 14:25:30 -070065 * Test case to check recycling behavior of KryoFactory.
66 */
67 @Test
68 public void testReallocation() {
69 final int poolSize = 3;
70 KryoFactory pool = new KryoFactory(poolSize);
71
72
73 // getting Kryo instance smaller than pool size should work just fine
74 Set<Kryo> kryos = new HashSet<>();
75 for (int i = 0; i < poolSize - 1; ++i) {
76 Kryo kryo = pool.newKryo();
77 assertNotNull(kryo);
78 assertTrue("KryoFactory should return unique instances",
79 kryos.add(kryo));
80 }
81
82 // recycle Kryo instance
83 for (Kryo kryo : kryos) {
84 pool.deleteKryo(kryo);
85 }
86
87
88 // recycling behavior check
89 Set<Kryo> kryos2 = new HashSet<>();
90 for (int i = 0; i < poolSize - 1; ++i) {
91 Kryo kryo = pool.newKryo();
92 assertNotNull(kryo);
93 assertTrue("KryoFactory should return unique instances",
94 kryos2.add(kryo));
95 }
96 // should at least have some recycled instances
97 assertTrue("Kryo instances should be reused after deleting",
98 !Sets.difference(kryos2, kryos).isEmpty());
99
100 for (Kryo kryo : kryos2) {
101 pool.deleteKryo(kryo);
102 }
103
104
105 // pool expansion behavior check
106 Set<Kryo> kryos3 = new HashSet<>();
107 // it should be able to get Kryo instances larger than pool size set
108 for (int i = 0; i < poolSize * 2; ++i) {
109 Kryo kryo = pool.newKryo();
110 assertNotNull(kryo);
111 assertTrue("KryoFactory should return unique instances",
112 kryos3.add(kryo));
113 }
114 // recycle Kryo instance (should trigger pool expansion)
115 for (Kryo kryo : kryos3) {
116 pool.deleteKryo(kryo);
117 }
118
119 // should at least have some Kryo instances we haven't seen initially.
120 assertTrue("New Kryo instances should be added to the pool",
121 !Sets.difference(kryos3, kryos).isEmpty());
122 }
123
124 /**
125 * Tests static serialize/deserialize methods.
126 */
127 @Test
128 public void testStaticSerializeDeserialize() {
129
130 final List<Object> objects = new ArrayList<>();
131 Dpid dpid1 = new Dpid(1);
132 PortNumber port10 = PortNumber.uint32(10);
133 SwitchPort switchPort = new SwitchPort(dpid1, port10);
134
135 objects.add(dpid1);
136 objects.add(port10);
137 objects.add(switchPort);
138
139 final byte[] bytes = KryoFactory.serialize(objects);
140 final List<Object> deserialized = KryoFactory.deserialize(bytes);
141
142 assertEquals(objects, deserialized);
143 }
144
145 /**
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700146 * Benchmark result.
147 */
148 private static final class Result {
149 /**
150 * Serialized type name.
151 */
152 String type;
153 /**
154 * Serialized size.
155 */
156 int size;
157 /**
158 * Serialization throughput (ops/sec).
159 */
160 double ser;
161 /**
162 * Deserialization throughput (ops/sec).
163 */
164 double deser;
165
166 public Result(String type, int size, double ser, double deser) {
167 this.type = type;
168 this.size = size;
169 this.ser = ser;
170 this.deser = deser;
171 }
172 }
173
174 private static enum EqualityCheck {
175 /**
176 * No way to compare equality provided.
177 */
178 IMPOSSIBLE,
179 /**
180 * custom equals method is defined.
181 */
182 EQUALS,
183 /**
184 * Can be compared using toString() result.
185 */
186 TO_STRING,
187 }
188
189 /**
190 * Benchmark serialization of specified object.
191 *
192 * @param obj the object to benchmark
193 * @param equalityCheck how to check equality of deserialized object.
194 * @return benchmark {@link Result}
195 */
196 private Result benchType(Object obj, EqualityCheck equalityCheck) {
197
198 Kryo kryo = kryoFactory.newKryo();
199 try {
200 byte[] buffer = new byte[1 * 1000 * 1000];
201 Output output = new Output(buffer);
202
203 // Measurement: serialization size
204 kryo.writeClassAndObject(output, obj);
205 int serializedBytes = output.toBytes().length;
206
207 // Measurement: serialization throughput
208 byte[] result = null;
209
210 long t1 = System.nanoTime();
211 for (int j = 0; j < NUM_ITERATIONS; j++) {
212 output.clear();
213 kryo.writeClassAndObject(output, obj);
214 result = output.toBytes();
215 }
216 long t2 = System.nanoTime();
217 double serTput = NUM_ITERATIONS * SEC_IN_NANO / (t2 - t1);
218
219 // Measurement: deserialization throughput
220 Object objOut = null;
221 Input input = new Input(result);
222 long t3 = System.nanoTime();
223 for (int j = 0; j < NUM_ITERATIONS; j++) {
224 input.setBuffer(result);
225 objOut = kryo.readClassAndObject(input);
226 }
227 long t4 = System.nanoTime();
228
229 switch (equalityCheck) {
230 case IMPOSSIBLE:
231 break;
232 case EQUALS:
233 assertEquals(obj, objOut);
234 break;
235 case TO_STRING:
236 assertEquals(obj.toString(), objOut.toString());
237 break;
238 default:
239 break;
240 }
241 double deserTput = NUM_ITERATIONS * SEC_IN_NANO / (t4 - t3);
242
243 return new Result(obj.getClass().getSimpleName(),
244 serializedBytes, serTput, deserTput);
245 } finally {
246 kryoFactory.deleteKryo(kryo);
247 }
248 }
249
250 /**
251 * Benchmark serialization of types registered to KryoFactory.
252 */
253 @Test
254 public void benchmark() throws Exception {
255
256 List<Result> results = new ArrayList<>();
257
258 // To be more strict, we should be checking serialized byte[].
259 { // CHECKSTYLE IGNORE THIS LINE
260 HostEvent obj = new HostEvent(MACAddress.valueOf(0x12345678));
Pavlin Radoslavova5637c02014-07-30 15:55:11 -0700261 obj.createStringAttribute(TopologyElement.TYPE,
262 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700263 obj.addAttachmentPoint(new SwitchPort(DPID_A, PORT_NO_A));
264 // avoid using System.currentTimeMillis() var-int size may change
265 obj.setLastSeenTime(392860800000L);
266 obj.freeze();
267 Result result = benchType(obj, EqualityCheck.EQUALS);
268 results.add(result);
269 // update me if serialized form is expected to change
270 assertEquals(43, result.size);
271 }
272
273 { // CHECKSTYLE IGNORE THIS LINE
Pavlin Radoslavova5637c02014-07-30 15:55:11 -0700274 LinkEvent obj = new LinkEvent(new SwitchPort(DPID_A, PORT_NO_A),
275 new SwitchPort(DPID_B, PORT_NO_B));
276 obj.createStringAttribute(TopologyElement.TYPE,
277 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700278 obj.freeze();
279 Result result = benchType(obj, EqualityCheck.EQUALS);
280 results.add(result);
281 // update me if serialized form is expected to change
282 assertEquals(49, result.size);
283 }
284
285 { // CHECKSTYLE IGNORE THIS LINE
286 PortEvent obj = new PortEvent(DPID_A, PORT_NO_A);
Pavlin Radoslavova5637c02014-07-30 15:55:11 -0700287 obj.createStringAttribute(TopologyElement.TYPE,
288 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700289 obj.freeze();
290 Result result = benchType(obj, EqualityCheck.EQUALS);
291 results.add(result);
292 // update me if serialized form is expected to change
293 assertEquals(24, result.size);
294 }
295
296 { // CHECKSTYLE IGNORE THIS LINE
297 SwitchEvent obj = new SwitchEvent(DPID_A);
Pavlin Radoslavova5637c02014-07-30 15:55:11 -0700298 obj.createStringAttribute(TopologyElement.TYPE,
299 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700300 obj.freeze();
301 Result result = benchType(obj, EqualityCheck.EQUALS);
302 results.add(result);
303 // update me if serialized form is expected to change
304 assertEquals(21, result.size);
305 }
306
307 { // CHECKSTYLE IGNORE THIS LINE
308 SwitchEvent evt = new SwitchEvent(DPID_A);
Pavlin Radoslavova5637c02014-07-30 15:55:11 -0700309 evt.createStringAttribute(TopologyElement.TYPE,
310 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700311 evt.freeze();
Pavlin Radoslavova5637c02014-07-30 15:55:11 -0700312 OnosInstanceId onosInstanceId =
313 new OnosInstanceId(ONOS_INSTANCE_NAME);
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700314
315 // using the back door to access package-scoped constructor
316 Constructor<TopologyEvent> swConst
Pavlin Radoslavova5637c02014-07-30 15:55:11 -0700317 = TopologyEvent.class.getDeclaredConstructor(SwitchEvent.class,
318 OnosInstanceId.class);
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700319 swConst.setAccessible(true);
Pavlin Radoslavova5637c02014-07-30 15:55:11 -0700320 TopologyEvent obj = swConst.newInstance(evt, onosInstanceId);
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700321
322 Result result = benchType(obj, EqualityCheck.TO_STRING);
323 results.add(result);
324 // update me if serialized form is expected to change
Pavlin Radoslavov31f85102014-08-15 13:55:44 -0700325 assertEquals(45, result.size);
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700326 }
327
Yuta HIGUCHI630216e2014-07-30 09:09:34 -0700328 { // CHECKSTYLE IGNORE THIS LINE
Pavlin Radoslavova5637c02014-07-30 15:55:11 -0700329 OnosInstanceId id = new OnosInstanceId(ONOS_INSTANCE_NAME);
Yuta HIGUCHI630216e2014-07-30 09:09:34 -0700330
331 Result result = benchType(id, EqualityCheck.EQUALS);
332 results.add(result);
333 // update me if serialized form is expected to change
334 assertEquals(21, result.size);
335 }
336
Pavlin Radoslavovdd08e8c2014-08-14 11:02:57 -0700337 { // CHECKSTYLE IGNORE THIS LINE
338 TopologyBatchOperation tbo = new TopologyBatchOperation();
339 OnosInstanceId onosInstanceId =
340 new OnosInstanceId(ONOS_INSTANCE_NAME);
341
342 // using the back door to access package-scoped constructor
343 Constructor<TopologyEvent> swConst
344 = TopologyEvent.class.getDeclaredConstructor(SwitchEvent.class,
345 OnosInstanceId.class);
346 swConst.setAccessible(true);
347
348 for (int i = 1; i <= 10; i++) {
349 Dpid dpid = new Dpid(i);
350 SwitchEvent switchEvent = new SwitchEvent(dpid);
351 TopologyEvent topologyEvent =
352 swConst.newInstance(switchEvent, onosInstanceId);
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700353 tbo.appendAddOperation(topologyEvent);
Pavlin Radoslavovdd08e8c2014-08-14 11:02:57 -0700354 }
355
356 Result result = benchType(tbo, EqualityCheck.EQUALS);
357 results.add(result);
358 // update me if serialized form is expected to change
Pavlin Radoslavov31f85102014-08-15 13:55:44 -0700359 assertEquals(186, result.size);
Pavlin Radoslavovdd08e8c2014-08-14 11:02:57 -0700360 }
361
Yuta HIGUCHI3d0ae0c2014-06-17 23:18:19 -0700362 // TODO Add registered classes we still use.
363
364
365 // Output for plot plugin
366 List<String> slabels = new ArrayList<>();
367 List<Number> svalues = new ArrayList<>();
368 List<String> tlabels = new ArrayList<>();
369 List<Number> tvalues = new ArrayList<>();
370
371 // Type, size, serialize T-put, deserialize T-put, N
372 System.out.println("Type, size, serialize T-put, deserialize T-put, N");
373
374 for (Result result : results) {
375 System.out.printf("%s, %d, %f, %f, %d\n",
376 result.type, result.size, result.ser, result.deser,
377 NUM_ITERATIONS);
378
379 // Output for plot plugin
380 // <Type>_size, <Type>_ser, <Type>_deser
381 slabels.addAll(Arrays.asList(
382 result.type + "_size"
383 ));
384 svalues.addAll(Arrays.asList(
385 result.size
386 ));
387 tlabels.addAll(Arrays.asList(
388 result.type + "_ser",
389 result.type + "_deser"
390 ));
391 tvalues.addAll(Arrays.asList(
392 result.ser,
393 result.deser
394 ));
395 }
396
397 // Output for plot plugin
398 PrintStream size = new PrintStream("target/KryoFactoryTest_size.csv");
399 PrintStream tput = new PrintStream("target/KryoFactoryTest_tput.csv");
400
401 for (String label : slabels) {
402 size.print(label);
403 size.print(", ");
404 }
405 size.println();
406 for (Number value : svalues) {
407 size.print(value);
408 size.print(", ");
409 }
410 size.close();
411
412 for (String label : tlabels) {
413 tput.print(label);
414 tput.print(", ");
415 }
416 tput.println();
417 for (Number value : tvalues) {
418 tput.print(value);
419 tput.print(", ");
420 }
421 tput.close();
422 }
423}