PageRenderTime 80ms CodeModel.GetById 40ms RepoModel.GetById 0ms app.codeStats 0ms

/tpc/src/serializers/BenchmarkRunner.java

https://github.com/thainb/jvm-serializers
Java | 1010 lines | 835 code | 116 blank | 59 comment | 128 complexity | e90b4640bde848a4208315ac7e7d6080 MD5 | raw file
  1. package serializers;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.IOException;
  6. import java.io.UnsupportedEncodingException;
  7. import java.io.PrintWriter;
  8. import java.io.StringWriter;
  9. import java.net.URLEncoder;
  10. import java.util.ArrayList;
  11. import java.util.Arrays;
  12. import java.util.Collections;
  13. import java.util.Comparator;
  14. import java.util.EnumMap;
  15. import java.util.HashMap;
  16. import java.util.HashSet;
  17. import java.util.LinkedHashMap;
  18. import java.util.Map;
  19. import java.util.Set;
  20. import java.util.regex.Pattern;
  21. import java.util.zip.DeflaterOutputStream;
  22. public class BenchmarkRunner
  23. {
  24. public final static int DEFAULT_ITERATIONS = 2000;
  25. public final static int DEFAULT_TRIALS = 20;
  26. /**
  27. * Number of milliseconds to warm up for each operation type for each serializer. Let's
  28. * start with 3 seconds.
  29. */
  30. final static long DEFAULT_WARMUP_MSECS = 3000;
  31. // These tests aren't included by default. Use the "-hidden" flag to enable them.
  32. private static final HashSet<String> HIDDEN = new HashSet<String>();
  33. static {
  34. // CKS is not included because it's not really publicly released.
  35. HIDDEN.add("cks");
  36. HIDDEN.add("cks-text");
  37. }
  38. private static final String ERROR_DIVIDER = "-------------------------------------------------------------------";
  39. public static void main(String[] args)
  40. {
  41. // --------------------------------------------------
  42. // Parse command-line options.
  43. Boolean filterIsInclude = null;
  44. Set<String> filterStrings = null;
  45. Integer iterations = null;
  46. Integer trials = null;
  47. Long warmupTime = null;
  48. boolean printChart = false;
  49. boolean prewarm = false;
  50. String dataFileName = null;
  51. boolean enableHidden = false;
  52. Set<String> optionsSeen = new HashSet<String>();
  53. for (String arg : args) {
  54. String remainder;
  55. if (arg.startsWith("--")) {
  56. remainder = arg.substring(2);
  57. }
  58. else if (arg.startsWith("-")) {
  59. remainder = arg.substring(1);
  60. }
  61. else if (dataFileName == null) {
  62. dataFileName = arg;
  63. continue;
  64. }
  65. else {
  66. System.err.println("Expecting only one non-option argument (<data-file> = \"" + dataFileName + "\").");
  67. System.err.println("Found a second one: \"" + arg + "\"");
  68. System.err.println("Use \"-help\" for usage information.");
  69. System.exit(1); return;
  70. }
  71. String option, value;
  72. int eqPos = remainder.indexOf('=');
  73. if (eqPos >= 0) {
  74. option = remainder.substring(0, eqPos);
  75. value = remainder.substring(eqPos+1);
  76. } else {
  77. option = remainder;
  78. value = null;
  79. }
  80. if (!optionsSeen.add(option)) {
  81. System.err.println("Repeated option: \"" + arg + "\"");
  82. System.exit(1); return;
  83. }
  84. if (option.equals("include")) {
  85. if (value == null) {
  86. System.err.println("The \"include\" option requires a value.");
  87. System.exit(1); return;
  88. }
  89. if (filterIsInclude == null) {
  90. filterIsInclude = true;
  91. filterStrings = new HashSet<String>(Arrays.asList(value.split(",")));
  92. } else {
  93. System.err.println("Can't use 'include' and 'exclude' options at the same time.");
  94. System.exit(1); return;
  95. }
  96. }
  97. else if (option.equals("exclude")) {
  98. if (value == null) {
  99. System.err.println("The \"exclude\" option requires a value.");
  100. System.exit(1); return;
  101. }
  102. if (filterIsInclude == null) {
  103. filterIsInclude = false;
  104. filterStrings = new HashSet<String>(Arrays.asList(value.split(",")));
  105. } else {
  106. System.err.println("Can't use 'include' and 'exclude' options at the same time.");
  107. System.exit(1); return;
  108. }
  109. }
  110. else if (option.equals("iterations")) {
  111. if (value == null) {
  112. System.err.println("The \"iterations\" option requires a value.");
  113. System.exit(1); return;
  114. }
  115. assert iterations == null;
  116. try {
  117. iterations = Integer.parseInt(value);
  118. } catch (NumberFormatException ex) {
  119. System.err.println("Invalid value for \"iterations\" option: \"" + value + "\"");
  120. System.exit(1); return;
  121. }
  122. if (iterations < 1) {
  123. System.err.println("Invalid value for \"iterations\" option: \"" + value + "\"");
  124. System.exit(1); return;
  125. }
  126. }
  127. else if (option.equals("trials")) {
  128. if (value == null) {
  129. System.err.println("The \"trials\" option requires a value.");
  130. System.exit(1); return;
  131. }
  132. assert trials == null;
  133. try {
  134. trials = Integer.parseInt(value);
  135. } catch (NumberFormatException ex) {
  136. System.err.println("Invalid value for \"trials\" option: \"" + value + "\"");
  137. System.exit(1); return;
  138. }
  139. if (trials < 1) {
  140. System.err.println("Invalid value for \"trials\" option: \"" + value + "\"");
  141. System.exit(1); return;
  142. }
  143. }
  144. else if (option.equals("warmup-time")) {
  145. if (value == null) {
  146. System.err.println("The \"warmup-time\" option requires a value.");
  147. System.exit(1); return;
  148. }
  149. assert warmupTime == null;
  150. try {
  151. warmupTime = Long.parseLong(value);
  152. } catch (NumberFormatException ex) {
  153. System.err.println("Invalid value for \"warmup-time\" option: \"" + value + "\"");
  154. System.exit(1); return;
  155. }
  156. if (warmupTime < 0) {
  157. System.err.println("Invalid value for \"warmup-time\" option: \"" + value + "\"");
  158. System.exit(1); return;
  159. }
  160. }
  161. else if (option.equals("pre-warmup")) {
  162. if (value != null) {
  163. System.err.println("The \"pre-warmup\" option does not take a value: \"" + arg + "\"");
  164. System.exit(1); return;
  165. }
  166. assert !prewarm;
  167. prewarm = true;
  168. }
  169. else if (option.equals("chart")) {
  170. if (value != null) {
  171. System.err.println("The \"chart\" option does not take a value: \"" + arg + "\"");
  172. System.exit(1); return;
  173. }
  174. assert !printChart;
  175. printChart = true;
  176. }
  177. else if (option.equals("hidden")) {
  178. if (value != null) {
  179. System.err.println("The \"hidden\" option does not take a value: \"" + arg + "\"");
  180. System.exit(1); return;
  181. }
  182. assert !enableHidden;
  183. enableHidden = true;
  184. }
  185. else if (option.equals("help")) {
  186. if (value != null) {
  187. System.err.println("The \"help\" option does not take a value: \"" + arg + "\"");
  188. System.exit(1); return;
  189. }
  190. if (args.length != 1) {
  191. System.err.println("The \"help\" option cannot be combined with any other option.");
  192. System.exit(1); return;
  193. }
  194. System.out.println();
  195. System.out.println("Usage: run [options] <data-file>");
  196. System.out.println();
  197. System.out.println("Options:");
  198. System.out.println(" -iterations=n [default=" + DEFAULT_ITERATIONS + "]");
  199. System.out.println(" -trials=n [default=" + DEFAULT_TRIALS + "]");
  200. System.out.println(" -warmup-time=millis [default=" + DEFAULT_WARMUP_MSECS + "]");
  201. System.out.println(" -pre-warmup (warm all serializers before the first measurement)");
  202. System.out.println(" -chart (generate a Google Chart URL for the results)");
  203. System.out.println(" -include=impl1,impl2,impl3,...");
  204. System.out.println(" -exclude=impl1,impl2,impl3,...");
  205. System.out.println(" -hidden (enable \"hidden\" serializers)");
  206. System.out.println(" -help");
  207. System.out.println();
  208. System.out.println("Example: run -chart -include=protobuf,thrift data/media.1.cks");
  209. System.out.println();
  210. System.exit(0); return;
  211. }
  212. else {
  213. System.err.println("Unknown option: \"" + arg + "\"");
  214. System.err.println("Use \"-help\" for usage information.");
  215. System.exit(1); return;
  216. }
  217. }
  218. if (iterations == null) iterations = DEFAULT_ITERATIONS;
  219. if (trials == null) trials = DEFAULT_TRIALS;
  220. if (warmupTime == null) warmupTime = DEFAULT_WARMUP_MSECS;
  221. if (dataFileName == null) {
  222. System.err.println("Missing <data-file> argument.");
  223. System.err.println("Use \"-help\" for usage information.");
  224. System.exit(1); return;
  225. }
  226. // --------------------------------------------------
  227. // Load serializers.
  228. TestGroups groups = new TestGroups();
  229. // Binary Formats; language-specific ones
  230. JavaBuiltIn.register(groups);
  231. JavaManual.register(groups);
  232. Scala.register(groups);
  233. // hessian and kryo are Java object serializations
  234. Hessian.register(groups);
  235. Kryo.register(groups);
  236. // Binary formats, generic: protobuf, thrift, avro, kryo, CKS, msgpack
  237. Protobuf.register(groups);
  238. ActiveMQProtobuf.register(groups);
  239. Protostuff.register(groups);
  240. Thrift.register(groups);
  241. AvroSpecific.register(groups);
  242. AvroGeneric.register(groups);
  243. CksBinary.register(groups);
  244. MsgPack.register(groups);
  245. // JSON
  246. JsonJacksonManual.register(groups);
  247. JsonJacksonManualTree.register(groups);
  248. JsonJacksonManualTreeWithStrings.register(groups);
  249. JsonJacksonDatabind.register(groups);
  250. JsonJacksonDatabindWithStrings.register(groups);
  251. JsonTwoLattes.register(groups);
  252. ProtostuffJson.register(groups);
  253. ProtobufJson.register(groups);
  254. GsonManual.register(groups);
  255. GsonManualTree.register(groups);
  256. Gson.register(groups);
  257. SvensonJsonDatabind.register(groups);
  258. FlexjsonDatabind.register(groups);
  259. JsonLibJsonDatabind.register(groups);
  260. FastJSONDatabind.register(groups);
  261. JsonSimpleManualTree.register(groups);
  262. JsonSmartManualTree.register(groups);
  263. JsonDotOrgManualTree.register(groups);
  264. JsonijJpath.register(groups);
  265. JsonijManualTree.register(groups);
  266. ArgoManualTree.register(groups);
  267. JsonPathDeserializerOnly.register(groups);
  268. // Then JSON-like
  269. // CKS text is textual JSON-like format
  270. CksText.register(groups);
  271. // then binary variants
  272. // BSON is binary JSON-like format
  273. BsonJackson.register(groups);
  274. BsonJacksonDatabind.register(groups);
  275. MongoDB.register(groups);
  276. // Smile is 1-to-1 binary representation of JSON
  277. JacksonSmileManual.register(groups);
  278. JacksonSmileDatabind.register(groups);
  279. ProtostuffSmile.register(groups);
  280. // XML-based formats.
  281. Stax.register(groups);
  282. XStream.register(groups);
  283. JacksonXmlDatabind.register(groups);
  284. JavolutionXml.register(groups);
  285. // --------------------------------------------------
  286. // Load data value.
  287. Object dataValue;
  288. TestGroup<?> group;
  289. {
  290. File dataFile = new File(dataFileName);
  291. if (!dataFile.exists()) {
  292. System.out.println("Couldn't find data file \"" + dataFile.getPath() + "\"");
  293. System.exit(1); return;
  294. }
  295. String[] parts = dataFile.getName().split("\\.");
  296. if (parts.length < 3) {
  297. System.out.println("Data file \"" + dataFile.getName() + "\" should be of the form \"<type>.<name>.<extension>\"");
  298. System.exit(1); return;
  299. }
  300. String dataType = parts[0];
  301. String extension = parts[parts.length-1];
  302. group = groups.groupMap.get(dataType);
  303. if (group == null) {
  304. System.out.println("Data file \"" + dataFileName + "\" can't be loaded.");
  305. System.out.println("Don't know about data type \"" + dataType + "\"");
  306. System.exit(1); return;
  307. }
  308. TestGroup.Entry<?,Object> loader = group.extensionMap.get(parts[parts.length-1]);
  309. if (loader == null) {
  310. System.out.println("Data file \"" + dataFileName + "\" can't be loaded.");
  311. System.out.println("No deserializer registered for data type \"" + dataType + "\" and file extension \"." + extension + "\"");
  312. System.exit(1); return;
  313. }
  314. Object deserialized;
  315. try {
  316. byte[] fileBytes = readFile(new File(dataFileName)); // Load entire file into a byte array.
  317. deserialized = loader.serializer.deserialize(fileBytes);
  318. }
  319. catch (Exception ex) {
  320. System.err.println("Error loading data from file \"" + dataFileName + "\".");
  321. System.err.println(ex.getMessage());
  322. System.exit(1); return;
  323. }
  324. dataValue = loader.transformer.reverse(deserialized);
  325. }
  326. @SuppressWarnings("unchecked")
  327. TestGroup<Object> group_ = (TestGroup<Object>) group;
  328. // --------------------------------------------------
  329. Set<String> matched = new HashSet<String>();
  330. Iterable<TestGroup.Entry<Object,Object>> available;
  331. if (enableHidden) {
  332. // Use all of them.
  333. available = group_.entries;
  334. } else {
  335. // Remove the hidden ones.
  336. ArrayList<TestGroup.Entry<Object,Object>> unhidden = new ArrayList<TestGroup.Entry<Object,Object>>();
  337. for (TestGroup.Entry<?,Object> entry_ : group.entries) {
  338. @SuppressWarnings("unchecked")
  339. TestGroup.Entry<Object,Object> entry = (TestGroup.Entry<Object,Object>) entry_;
  340. String name = entry.serializer.getName();
  341. if (!HIDDEN.contains(name)) unhidden.add(entry);
  342. }
  343. available = unhidden;
  344. }
  345. Iterable<TestGroup.Entry<Object,Object>> matchingEntries;
  346. if (filterStrings == null) {
  347. matchingEntries = available;
  348. }
  349. else {
  350. ArrayList<TestGroup.Entry<Object,Object>> al = new ArrayList<TestGroup.Entry<Object,Object>>();
  351. matchingEntries = al;
  352. for (TestGroup.Entry<?,Object> entry_ : available) {
  353. @SuppressWarnings("unchecked")
  354. TestGroup.Entry<Object,Object> entry = (TestGroup.Entry<Object,Object>) entry_;
  355. String name = entry.serializer.getName();
  356. // See if any of the filters match.
  357. boolean found = false;
  358. for (String s : filterStrings) {
  359. boolean thisOneMatches = match(s, name);
  360. if (thisOneMatches) {
  361. matched.add(s);
  362. found = true;
  363. }
  364. }
  365. if (found == filterIsInclude) {
  366. al.add(entry);
  367. }
  368. }
  369. Set<String> unmatched = new HashSet<String>(filterStrings);
  370. unmatched.removeAll(matched);
  371. for (String s : unmatched) {
  372. System.err.println("Warning: there is no implementation name matching the pattern \"" + s + "\"");
  373. if (!enableHidden) {
  374. for (String hiddenName : HIDDEN) {
  375. if (match(s, hiddenName)) {
  376. System.err.println("(The \"" + hiddenName + "\", serializer is hidden by default.");
  377. System.err.println(" Use the \"-hidden\" option to enable hidden serializers)");
  378. break;
  379. }
  380. }
  381. }
  382. }
  383. }
  384. EnumMap<measurements, Map<String, Double>> values;
  385. StringWriter errors = new StringWriter();
  386. PrintWriter errorsPW = new PrintWriter(errors);
  387. try {
  388. values = start(errorsPW, iterations, trials, warmupTime, prewarm, matchingEntries, dataValue);
  389. }
  390. catch (Exception ex) {
  391. ex.printStackTrace(System.err);
  392. System.exit(1); return;
  393. }
  394. if (printChart) {
  395. printImages(values);
  396. }
  397. // Print errors after chart. That way you can't miss it.
  398. String errorsString = errors.toString();
  399. if (errorsString.length() > 0) {
  400. System.out.println(ERROR_DIVIDER);
  401. System.out.println("Errors occurred during benchmarking:");
  402. System.out.print(errorsString);
  403. System.exit(1); return;
  404. }
  405. }
  406. private static boolean match(String pattern, String name)
  407. {
  408. StringBuilder regex = new StringBuilder();
  409. while (pattern.length() > 0) {
  410. int starPos = pattern.indexOf('*');
  411. if (starPos < 0) {
  412. regex.append(Pattern.quote(pattern));
  413. break;
  414. }
  415. else {
  416. String beforeStar = pattern.substring(0, starPos);
  417. String afterStar = pattern.substring(starPos + 1);
  418. regex.append(Pattern.quote(beforeStar));
  419. regex.append(".*");
  420. pattern = afterStar;
  421. }
  422. }
  423. return Pattern.matches(regex.toString(), name);
  424. }
  425. // ------------------------------------------------------------------------------------
  426. private static byte[] readFile(File file)
  427. throws IOException
  428. {
  429. FileInputStream fin = new FileInputStream(file);
  430. try {
  431. ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
  432. byte[] data = new byte[1024];
  433. while (true) {
  434. int numBytes = fin.read(data);
  435. if (numBytes < 0) break;
  436. baos.write(data, 0, numBytes);
  437. }
  438. return baos.toByteArray();
  439. }
  440. finally {
  441. fin.close();
  442. }
  443. }
  444. // ------------------------------------------------------------------------------------
  445. private static double iterationTime(long delta, int iterations)
  446. {
  447. return (double) delta / (double) (iterations);
  448. }
  449. private static final TestCase Create = new TestCase()
  450. {
  451. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  452. {
  453. long start = System.nanoTime();
  454. for (int i = 0; i < iterations; i++)
  455. {
  456. transformer.forward(value);
  457. }
  458. return iterationTime(System.nanoTime() - start, iterations);
  459. }
  460. };
  461. private static final TestCase Serialize = new TestCase()
  462. {
  463. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  464. {
  465. long start = System.nanoTime();
  466. for (int i = 0; i < iterations; i++)
  467. {
  468. Object obj = transformer.forward(value);
  469. serializer.serialize(obj);
  470. }
  471. return iterationTime(System.nanoTime() - start, iterations);
  472. }
  473. };
  474. private static final TestCase SerializeSameObject = new TestCase()
  475. {
  476. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  477. {
  478. // let's reuse same instance to reduce overhead
  479. Object obj = transformer.forward(value);
  480. long start = System.nanoTime();
  481. for (int i = 0; i < iterations; i++)
  482. {
  483. serializer.serialize(obj);
  484. //if (i % 1000 == 0)
  485. // doGc();
  486. }
  487. return iterationTime(System.nanoTime() - start, iterations);
  488. }
  489. };
  490. private static final TestCase Deserialize = new TestCase()
  491. {
  492. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  493. {
  494. byte[] array = serializer.serialize(transformer.forward(value));
  495. long start = System.nanoTime();
  496. for (int i = 0; i < iterations; i++)
  497. {
  498. serializer.deserialize(array);
  499. }
  500. return iterationTime(System.nanoTime() - start, iterations);
  501. }
  502. };
  503. private static final TestCase DeserializeAndCheck = new TestCase()
  504. {
  505. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  506. {
  507. byte[] array = serializer.serialize(transformer.forward(value));
  508. long start = System.nanoTime();
  509. for (int i = 0; i < iterations; i++)
  510. {
  511. Object obj = serializer.deserialize(array);
  512. transformer.reverse(obj);
  513. }
  514. return iterationTime(System.nanoTime() - start, iterations);
  515. }
  516. };
  517. private static final TestCase DeserializeAndCheckShallow = new TestCase()
  518. {
  519. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  520. {
  521. byte[] array = serializer.serialize(transformer.forward(value));
  522. long start = System.nanoTime();
  523. for (int i = 0; i < iterations; i++)
  524. {
  525. Object obj = serializer.deserialize(array);
  526. transformer.shallowReverse(obj);
  527. }
  528. return iterationTime(System.nanoTime() - start, iterations);
  529. }
  530. };
  531. /**
  532. * JVM is not required to honor GC requests, but adding bit of sleep around request is
  533. * most likely to give it a chance to do it.
  534. */
  535. private static void doGc()
  536. {
  537. try {
  538. Thread.sleep(50L);
  539. } catch (InterruptedException ie) {
  540. System.err.println("Interrupted while sleeping in serializers.BenchmarkRunner.doGc()");
  541. }
  542. System.gc();
  543. try { // longer sleep afterwards (not needed by GC, but may help with scheduling)
  544. Thread.sleep(200L);
  545. } catch (InterruptedException ie) {
  546. System.err.println("Interrupted while sleeping in serializers.BenchmarkRunner.doGc()");
  547. }
  548. }
  549. // ------------------------------------------------------------------------------------
  550. private static abstract class TestCase
  551. {
  552. public abstract <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception;
  553. }
  554. private static final class TestCaseRunner<J>
  555. {
  556. private final Transformer<J,Object> transformer;
  557. private final Serializer<Object> serializer;
  558. private final J value;
  559. public TestCaseRunner(Transformer<J,Object> transformer, Serializer<Object> serializer, J value)
  560. {
  561. this.transformer = transformer;
  562. this.serializer = serializer;
  563. this.value = value;
  564. }
  565. public double run(TestCase tc, int iterations) throws Exception
  566. {
  567. return tc.run(transformer, serializer, value, iterations);
  568. }
  569. public double runTakeMin(int trials, TestCase tc, int iterations) throws Exception
  570. {
  571. double minTime = Double.MAX_VALUE;
  572. for (int i = 0; i < trials; i++) {
  573. double time = tc.run(transformer, serializer, value, iterations);
  574. minTime = Math.min(minTime, time);
  575. }
  576. return minTime;
  577. }
  578. }
  579. enum measurements
  580. {
  581. timeCreate("create (nanos)"), timeSerializeDifferentObjects("ser (nanos)"), timeSerializeSameObject("ser+same (nanos)"),
  582. timeDeserializeNoFieldAccess("deser (nanos)"), timeDeserializeAndCheck("deser+deep (nanos)"), timeDeserializeAndCheckShallow("deser+shal (nanos)"),
  583. totalTime("total (nanos)"), length("size (bytes)"), lengthDeflate("size+dfl (bytes)"),
  584. ;
  585. public final String displayName;
  586. measurements(String displayName)
  587. {
  588. this.displayName = displayName;
  589. }
  590. }
  591. private static <J> EnumMap<measurements, Map<String, Double>>
  592. start(PrintWriter errors, int iterations, int trials, long warmupTime, boolean prewarm, Iterable<TestGroup.Entry<J,Object>> groups, J value) throws Exception
  593. {
  594. // Check correctness first.
  595. System.out.println("Checking correctness...");
  596. for (TestGroup.Entry<J,Object> entry : groups)
  597. {
  598. checkCorrectness(errors, entry.transformer, entry.serializer, value);
  599. }
  600. System.out.println("[done]");
  601. // Pre-warm.
  602. if (prewarm) {
  603. System.out.print("Pre-warmup...");
  604. for (TestGroup.Entry<J,Object> entry : groups)
  605. {
  606. TestCaseRunner<J> runner = new TestCaseRunner<J>(entry.transformer, entry.serializer, value);
  607. String name = entry.serializer.getName();
  608. System.out.print(" " + name);
  609. warmCreation(runner, warmupTime);
  610. warmSerialization(runner, warmupTime);
  611. warmDeserialization(runner, warmupTime);
  612. }
  613. System.out.println();
  614. System.out.println("[done]");
  615. }
  616. System.out.printf("%-32s %6s %7s %7s %7s %7s %7s %7s %6s %5s\n",
  617. "",
  618. "create",
  619. "ser",
  620. "+same",
  621. "deser",
  622. "+shal",
  623. "+deep",
  624. "total",
  625. "size",
  626. "+dfl");
  627. EnumMap<measurements, Map<String, Double>> values = new EnumMap<measurements, Map<String, Double>>(measurements.class);
  628. for (measurements m : measurements.values())
  629. values.put(m, new HashMap<String, Double>());
  630. // Actual tests.
  631. for (TestGroup.Entry<J,Object> entry : groups)
  632. {
  633. TestCaseRunner<J> runner = new TestCaseRunner<J>(entry.transformer, entry.serializer, value);
  634. String name = entry.serializer.getName();
  635. try {
  636. /*
  637. * Should only warm things for the serializer that we test next: HotSpot JIT will
  638. * otherwise spent most of its time optimizing slower ones... Use
  639. * -XX:CompileThreshold=1 to hint the JIT to start immediately
  640. *
  641. * Actually: 1 is often not a good value -- threshold is the number
  642. * of samples needed to trigger inlining, and there's no point in
  643. * inlining everything. Default value is in thousands, so lowering
  644. * it to, say, 1000 is usually better.
  645. */
  646. warmCreation(runner, warmupTime);
  647. doGc();
  648. double timeCreate = runner.runTakeMin(trials, Create, iterations * 100); // do more iteration for object creation because of its short time
  649. warmSerialization(runner, warmupTime);
  650. doGc();
  651. double timeSerializeDifferentObjects = runner.runTakeMin(trials, Serialize, iterations);
  652. doGc();
  653. double timeSerializeSameObject = runner.runTakeMin(trials, SerializeSameObject, iterations);
  654. warmDeserialization(runner, warmupTime);
  655. doGc();
  656. double timeDeserializeNoFieldAccess = runner.runTakeMin(trials, Deserialize, iterations);
  657. doGc();
  658. double timeDeserializeAndCheckShallow = runner.runTakeMin(trials, DeserializeAndCheckShallow, iterations);
  659. doGc();
  660. double timeDeserializeAndCheck = runner.runTakeMin(trials, DeserializeAndCheck, iterations);
  661. double totalTime = timeSerializeDifferentObjects + timeDeserializeAndCheck;
  662. byte[] array = entry.serializer.serialize(entry.transformer.forward(value));
  663. byte[] compressDeflate = compressDeflate(array);
  664. System.out.printf("%-32s %6.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %6d %5d\n",
  665. name,
  666. timeCreate,
  667. timeSerializeDifferentObjects,
  668. timeSerializeSameObject,
  669. timeDeserializeNoFieldAccess,
  670. timeDeserializeAndCheckShallow,
  671. timeDeserializeAndCheck,
  672. totalTime,
  673. array.length,
  674. compressDeflate.length);
  675. addValue(values, name, timeCreate, timeSerializeDifferentObjects, timeSerializeSameObject,
  676. timeDeserializeNoFieldAccess, timeDeserializeAndCheckShallow, timeDeserializeAndCheck, totalTime,
  677. array.length, compressDeflate.length);
  678. }
  679. catch (Exception ex) {
  680. System.out.println("ERROR: \"" + name + "\" crashed during benchmarking.");
  681. errors.println(ERROR_DIVIDER);
  682. errors.println("\"" + name + "\" crashed during benchmarking.");
  683. ex.printStackTrace(errors);
  684. }
  685. }
  686. return values;
  687. }
  688. private static byte[] compressDeflate(byte[] data)
  689. {
  690. try {
  691. ByteArrayOutputStream bout = new ByteArrayOutputStream(500);
  692. DeflaterOutputStream compresser = new DeflaterOutputStream(bout);
  693. compresser.write(data, 0, data.length);
  694. compresser.finish();
  695. compresser.flush();
  696. return bout.toByteArray();
  697. }
  698. catch (IOException ex) {
  699. AssertionError ae = new AssertionError("IOException while writing to ByteArrayOutputStream!");
  700. ae.initCause(ex);
  701. throw ae;
  702. }
  703. }
  704. /**
  705. * Method that tries to validate correctness of serializer, using
  706. * round-trip (construct, serializer, deserialize; compare objects
  707. * after steps 1 and 3).
  708. */
  709. private static <J> void checkCorrectness(PrintWriter errors, Transformer<J,Object> transformer, Serializer<Object> serializer, J value)
  710. throws Exception
  711. {
  712. Object specialInput;
  713. String name = serializer.getName();
  714. try {
  715. specialInput = transformer.forward(value);
  716. }
  717. catch (Exception ex) {
  718. System.out.println("ERROR: \"" + name + "\" crashed during forward transformation.");
  719. errors.println(ERROR_DIVIDER);
  720. errors.println("\"" + name + "\" crashed during forward transformation.");
  721. ex.printStackTrace(errors);
  722. return;
  723. }
  724. byte[] array;
  725. try {
  726. array = serializer.serialize(specialInput);
  727. }
  728. catch (Exception ex) {
  729. System.out.println("ERROR: \"" + name + "\" crashed during serialization.");
  730. errors.println(ERROR_DIVIDER);
  731. errors.println("\"" + name + "\" crashed during serialization.");
  732. ex.printStackTrace(errors);
  733. return;
  734. }
  735. Object specialOutput;
  736. try {
  737. specialOutput = serializer.deserialize(array);
  738. }
  739. catch (Exception ex) {
  740. System.out.println("ERROR: \"" + name + "\" crashed during deserialization.");
  741. errors.println(ERROR_DIVIDER);
  742. errors.println("\"" + name + "\" crashed during deserialization.");
  743. ex.printStackTrace(errors);
  744. return;
  745. }
  746. J output;
  747. try {
  748. output = transformer.reverse(specialOutput);
  749. }
  750. catch (Exception ex) {
  751. System.out.println("ERROR: \"" + name + "\" crashed during reverse transformation.");
  752. errors.println(ERROR_DIVIDER);
  753. errors.println("\"" + name + "\" crashed during reverse transformation.");
  754. ex.printStackTrace(errors);
  755. return;
  756. }
  757. if (!value.equals(output)) {
  758. System.out.println("ERROR: \"" + name + "\" failed round-trip check.");
  759. errors.println(ERROR_DIVIDER);
  760. errors.println("\"" + name + "\" failed round-trip check.");
  761. errors.println("ORIGINAL: " + value);
  762. errors.println("ROUNDTRIP: " + output);
  763. }
  764. }
  765. private static void printImages(EnumMap<measurements, Map<String, Double>> values)
  766. {
  767. for (measurements m : values.keySet()) {
  768. Map<String, Double> map = values.get(m);
  769. ArrayList<Map.Entry<String,Double>> list = new ArrayList<Map.Entry<String,Double>>(map.entrySet());
  770. Collections.sort(list, new Comparator<Map.Entry<String,Double>>() {
  771. public int compare (Map.Entry<String,Double> o1, Map.Entry<String,Double> o2) {
  772. double diff = o1.getValue() - o2.getValue();
  773. return diff > 0 ? 1 : (diff < 0 ? -1 : 0);
  774. }
  775. });
  776. LinkedHashMap<String, Double> sortedMap = new LinkedHashMap<String, Double>();
  777. for (Map.Entry<String, Double> entry : list) {
  778. if( !entry.getValue().isNaN() ) {
  779. sortedMap.put(entry.getKey(), entry.getValue());
  780. }
  781. }
  782. if (!sortedMap.isEmpty()) printImage(sortedMap, m);
  783. }
  784. }
  785. private static String urlEncode(String s)
  786. {
  787. try {
  788. return URLEncoder.encode(s, "UTF-8");
  789. }
  790. catch (UnsupportedEncodingException e) {
  791. throw new RuntimeException(e);
  792. }
  793. }
  794. private static void printImage(Map<String, Double> map, measurements m)
  795. {
  796. StringBuilder valSb = new StringBuilder();
  797. String names = "";
  798. double max = Double.MIN_NORMAL;
  799. for (Map.Entry<String, Double> entry : map.entrySet())
  800. {
  801. double value = entry.getValue();
  802. valSb.append((int) value).append(',');
  803. max = Math.max(max, entry.getValue());
  804. names = urlEncode(entry.getKey()) + '|' + names;
  805. }
  806. int headerSize = 30;
  807. int maxPixels = 300 * 1000; // Limit set by Google's Chart API.
  808. int maxHeight = 600;
  809. int width = maxPixels / maxHeight;
  810. int barThickness = 10;
  811. int barSpacing = 10;
  812. int height;
  813. // Reduce bar thickness and spacing until we can fit in the maximum height.
  814. while (true) {
  815. height = headerSize + map.size()*(barThickness + barSpacing);
  816. if (height <= maxHeight) break;
  817. barSpacing--;
  818. if (barSpacing == 1) break;
  819. height = headerSize + map.size()*(barThickness + barSpacing);
  820. if (height <= maxHeight) break;
  821. barThickness--;
  822. if (barThickness == 1) break;
  823. }
  824. boolean truncated = false;
  825. if (height > maxHeight) {
  826. truncated = true;
  827. height = maxHeight;
  828. }
  829. double scale = max * 1.1;
  830. System.out.println("<img src='https://chart.googleapis.com/chart?chtt="
  831. + urlEncode(m.displayName)
  832. + "&chf=c||lg||0||FFFFFF||1||76A4FB||0|bg||s||EFEFEF&chs="+width+"x"+height+"&chd=t:"
  833. + valSb.toString().substring(0, valSb.length() - 1)
  834. + "&chds=0,"+ scale
  835. + "&chxt=y"
  836. + "&chxl=0:|" + names.substring(0, names.length() - 1)
  837. + "&chm=N *f*,000000,0,-1,10&lklk&chdlp=t&chco=660000|660033|660066|660099|6600CC|6600FF|663300|663333|663366|663399|6633CC|6633FF|666600|666633|666666&cht=bhg&chbh=" + barThickness + ",0," + barSpacing + "&nonsense=aaa.png'/>");
  838. if (truncated) {
  839. System.err.println("WARNING: Not enough room to fit all bars in chart.");
  840. }
  841. }
  842. private static void addValue(
  843. EnumMap<measurements, Map<String, Double>> values,
  844. String name,
  845. double timeCreate,
  846. double timeSerializeDifferentObjects,
  847. double timeSerializeSameObject,
  848. double timeDeserializeNoFieldAccess,
  849. double timeDeserializeAndCheckShallow,
  850. double timeDeserializeAndCheck,
  851. double totalTime,
  852. double length, double lengthDeflate)
  853. {
  854. values.get(measurements.timeSerializeDifferentObjects).put(name, timeSerializeDifferentObjects);
  855. values.get(measurements.timeSerializeSameObject).put(name, timeSerializeSameObject);
  856. values.get(measurements.timeDeserializeNoFieldAccess).put(name, timeDeserializeNoFieldAccess);
  857. values.get(measurements.timeDeserializeAndCheckShallow).put(name, timeDeserializeAndCheckShallow);
  858. values.get(measurements.timeDeserializeAndCheck).put(name, timeDeserializeAndCheck);
  859. values.get(measurements.totalTime).put(name, totalTime);
  860. values.get(measurements.length).put(name, length);
  861. values.get(measurements.lengthDeflate).put(name, lengthDeflate);
  862. values.get(measurements.timeCreate).put(name, timeCreate);
  863. }
  864. private static <J> void warmCreation(TestCaseRunner<J> runner, long warmupTime) throws Exception
  865. {
  866. // Instead of fixed counts, let's try to prime by running for N seconds
  867. long endTime = System.currentTimeMillis() + warmupTime;
  868. do
  869. {
  870. runner.run(Create, 10);
  871. }
  872. while (System.currentTimeMillis() < endTime);
  873. }
  874. private static <J> void warmSerialization(TestCaseRunner<J> runner, long warmupTime) throws Exception
  875. {
  876. // Instead of fixed counts, let's try to prime by running for N seconds
  877. long endTime = System.currentTimeMillis() + warmupTime;
  878. do
  879. {
  880. runner.run(Serialize, 10);
  881. }
  882. while (System.currentTimeMillis() < endTime);
  883. }
  884. private static <J> void warmDeserialization(TestCaseRunner<J> runner, long warmupTime) throws Exception
  885. {
  886. // Instead of fixed counts, let's try to prime by running for N seconds
  887. long endTime = System.currentTimeMillis() + warmupTime;
  888. do
  889. {
  890. runner.run(DeserializeAndCheck, 10);
  891. }
  892. while (System.currentTimeMillis() < endTime);
  893. }
  894. }