PageRenderTime 54ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/tpc/src/serializers/BenchmarkRunner.java

https://github.com/cutting/jvm-serializers
Java | 992 lines | 818 code | 115 blank | 59 comment | 128 complexity | 8a8a9592771461eaa1f0e59194154349 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
  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. // JSON
  245. JsonJacksonManual.register(groups);
  246. JsonJacksonDatabind.register(groups);
  247. JsonTwoLattes.register(groups);
  248. ProtostuffJson.register(groups);
  249. ProtobufJson.register(groups);
  250. Gson.register(groups);
  251. // Then JSON-like
  252. // CKS text is textual JSON-like format
  253. CksText.register(groups);
  254. // then binary variants
  255. // BSON is binary JSON-like format
  256. BsonJackson.register(groups);
  257. BsonJacksonDatabind.register(groups);
  258. MongoDB.register(groups);
  259. // Smile is 1-to-1 binary representation of JSON
  260. JacksonSmileManual.register(groups);
  261. JacksonSmileDatabind.register(groups);
  262. ProtostuffSmile.register(groups);
  263. // XML-based formats.
  264. Stax.register(groups);
  265. XStream.register(groups);
  266. JacksonXmlDatabind.register(groups);
  267. JavolutionXml.register(groups);
  268. // --------------------------------------------------
  269. // Load data value.
  270. Object dataValue;
  271. TestGroup<?> group;
  272. {
  273. File dataFile = new File(dataFileName);
  274. if (!dataFile.exists()) {
  275. System.out.println("Couldn't find data file \"" + dataFile.getPath() + "\"");
  276. System.exit(1); return;
  277. }
  278. String[] parts = dataFile.getName().split("\\.");
  279. if (parts.length < 3) {
  280. System.out.println("Data file \"" + dataFile.getName() + "\" should be of the form \"<type>.<name>.<extension>\"");
  281. System.exit(1); return;
  282. }
  283. String dataType = parts[0];
  284. String extension = parts[parts.length-1];
  285. group = groups.groupMap.get(dataType);
  286. if (group == null) {
  287. System.out.println("Data file \"" + dataFileName + "\" can't be loaded.");
  288. System.out.println("Don't know about data type \"" + dataType + "\"");
  289. System.exit(1); return;
  290. }
  291. TestGroup.Entry<?,Object> loader = group.extensionMap.get(parts[parts.length-1]);
  292. if (loader == null) {
  293. System.out.println("Data file \"" + dataFileName + "\" can't be loaded.");
  294. System.out.println("No deserializer registered for data type \"" + dataType + "\" and file extension \"." + extension + "\"");
  295. System.exit(1); return;
  296. }
  297. Object deserialized;
  298. try {
  299. byte[] fileBytes = readFile(new File(dataFileName)); // Load entire file into a byte array.
  300. deserialized = loader.serializer.deserialize(fileBytes);
  301. }
  302. catch (Exception ex) {
  303. System.err.println("Error loading data from file \"" + dataFileName + "\".");
  304. System.err.println(ex.getMessage());
  305. System.exit(1); return;
  306. }
  307. dataValue = loader.transformer.reverse(deserialized);
  308. }
  309. @SuppressWarnings("unchecked")
  310. TestGroup<Object> group_ = (TestGroup<Object>) group;
  311. // --------------------------------------------------
  312. Set<String> matched = new HashSet<String>();
  313. Iterable<TestGroup.Entry<Object,Object>> available;
  314. if (enableHidden) {
  315. // Use all of them.
  316. available = group_.entries;
  317. } else {
  318. // Remove the hidden ones.
  319. ArrayList<TestGroup.Entry<Object,Object>> unhidden = new ArrayList<TestGroup.Entry<Object,Object>>();
  320. for (TestGroup.Entry<?,Object> entry_ : group.entries) {
  321. @SuppressWarnings("unchecked")
  322. TestGroup.Entry<Object,Object> entry = (TestGroup.Entry<Object,Object>) entry_;
  323. String name = entry.serializer.getName();
  324. if (!HIDDEN.contains(name)) unhidden.add(entry);
  325. }
  326. available = unhidden;
  327. }
  328. Iterable<TestGroup.Entry<Object,Object>> matchingEntries;
  329. if (filterStrings == null) {
  330. matchingEntries = available;
  331. }
  332. else {
  333. ArrayList<TestGroup.Entry<Object,Object>> al = new ArrayList<TestGroup.Entry<Object,Object>>();
  334. matchingEntries = al;
  335. for (TestGroup.Entry<?,Object> entry_ : available) {
  336. @SuppressWarnings("unchecked")
  337. TestGroup.Entry<Object,Object> entry = (TestGroup.Entry<Object,Object>) entry_;
  338. String name = entry.serializer.getName();
  339. // See if any of the filters match.
  340. boolean found = false;
  341. for (String s : filterStrings) {
  342. boolean thisOneMatches = match(s, name);
  343. if (thisOneMatches) {
  344. matched.add(s);
  345. found = true;
  346. }
  347. }
  348. if (found == filterIsInclude) {
  349. al.add(entry);
  350. }
  351. }
  352. Set<String> unmatched = new HashSet<String>(filterStrings);
  353. unmatched.removeAll(matched);
  354. for (String s : unmatched) {
  355. System.err.println("Warning: there is no implementation name matching the pattern \"" + s + "\"");
  356. if (!enableHidden) {
  357. for (String hiddenName : HIDDEN) {
  358. if (match(s, hiddenName)) {
  359. System.err.println("(The \"" + hiddenName + "\", serializer is hidden by default.");
  360. System.err.println(" Use the \"-hidden\" option to enable hidden serializers)");
  361. break;
  362. }
  363. }
  364. }
  365. }
  366. }
  367. EnumMap<measurements, Map<String, Double>> values;
  368. StringWriter errors = new StringWriter();
  369. PrintWriter errorsPW = new PrintWriter(errors);
  370. try {
  371. values = start(errorsPW, iterations, trials, warmupTime, prewarm, matchingEntries, dataValue);
  372. }
  373. catch (Exception ex) {
  374. ex.printStackTrace(System.err);
  375. System.exit(1); return;
  376. }
  377. if (printChart) {
  378. printImages(values);
  379. }
  380. // Print errors after chart. That way you can't miss it.
  381. String errorsString = errors.toString();
  382. if (errorsString.length() > 0) {
  383. System.out.println(ERROR_DIVIDER);
  384. System.out.println("Errors occurred during benchmarking:");
  385. System.out.print(errorsString);
  386. System.exit(1); return;
  387. }
  388. }
  389. private static boolean match(String pattern, String name)
  390. {
  391. StringBuilder regex = new StringBuilder();
  392. while (pattern.length() > 0) {
  393. int starPos = pattern.indexOf('*');
  394. if (starPos < 0) {
  395. regex.append(Pattern.quote(pattern));
  396. break;
  397. }
  398. else {
  399. String beforeStar = pattern.substring(0, starPos);
  400. String afterStar = pattern.substring(starPos + 1);
  401. regex.append(Pattern.quote(beforeStar));
  402. regex.append(".*");
  403. pattern = afterStar;
  404. }
  405. }
  406. return Pattern.matches(regex.toString(), name);
  407. }
  408. // ------------------------------------------------------------------------------------
  409. private static byte[] readFile(File file)
  410. throws IOException
  411. {
  412. FileInputStream fin = new FileInputStream(file);
  413. try {
  414. ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
  415. byte[] data = new byte[1024];
  416. while (true) {
  417. int numBytes = fin.read(data);
  418. if (numBytes < 0) break;
  419. baos.write(data, 0, numBytes);
  420. }
  421. return baos.toByteArray();
  422. }
  423. finally {
  424. fin.close();
  425. }
  426. }
  427. // ------------------------------------------------------------------------------------
  428. private static double iterationTime(long delta, int iterations)
  429. {
  430. return (double) delta / (double) (iterations);
  431. }
  432. private static final TestCase Create = new TestCase()
  433. {
  434. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  435. {
  436. long start = System.nanoTime();
  437. for (int i = 0; i < iterations; i++)
  438. {
  439. transformer.forward(value);
  440. }
  441. return iterationTime(System.nanoTime() - start, iterations);
  442. }
  443. };
  444. private static final TestCase Serialize = new TestCase()
  445. {
  446. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  447. {
  448. long start = System.nanoTime();
  449. for (int i = 0; i < iterations; i++)
  450. {
  451. Object obj = transformer.forward(value);
  452. serializer.serialize(obj);
  453. }
  454. return iterationTime(System.nanoTime() - start, iterations);
  455. }
  456. };
  457. private static final TestCase SerializeSameObject = new TestCase()
  458. {
  459. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  460. {
  461. // let's reuse same instance to reduce overhead
  462. Object obj = transformer.forward(value);
  463. long start = System.nanoTime();
  464. for (int i = 0; i < iterations; i++)
  465. {
  466. serializer.serialize(obj);
  467. //if (i % 1000 == 0)
  468. // doGc();
  469. }
  470. return iterationTime(System.nanoTime() - start, iterations);
  471. }
  472. };
  473. private static final TestCase Deserialize = new TestCase()
  474. {
  475. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  476. {
  477. byte[] array = serializer.serialize(transformer.forward(value));
  478. long start = System.nanoTime();
  479. for (int i = 0; i < iterations; i++)
  480. {
  481. serializer.deserialize(array);
  482. }
  483. return iterationTime(System.nanoTime() - start, iterations);
  484. }
  485. };
  486. private static final TestCase DeserializeAndCheck = new TestCase()
  487. {
  488. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  489. {
  490. byte[] array = serializer.serialize(transformer.forward(value));
  491. long start = System.nanoTime();
  492. for (int i = 0; i < iterations; i++)
  493. {
  494. Object obj = serializer.deserialize(array);
  495. transformer.reverse(obj);
  496. }
  497. return iterationTime(System.nanoTime() - start, iterations);
  498. }
  499. };
  500. private static final TestCase DeserializeAndCheckShallow = new TestCase()
  501. {
  502. public <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception
  503. {
  504. byte[] array = serializer.serialize(transformer.forward(value));
  505. long start = System.nanoTime();
  506. for (int i = 0; i < iterations; i++)
  507. {
  508. Object obj = serializer.deserialize(array);
  509. transformer.shallowReverse(obj);
  510. }
  511. return iterationTime(System.nanoTime() - start, iterations);
  512. }
  513. };
  514. /**
  515. * JVM is not required to honor GC requests, but adding bit of sleep around request is
  516. * most likely to give it a chance to do it.
  517. */
  518. private static void doGc()
  519. {
  520. try {
  521. Thread.sleep(50L);
  522. } catch (InterruptedException ie) {
  523. System.err.println("Interrupted while sleeping in serializers.BenchmarkRunner.doGc()");
  524. }
  525. System.gc();
  526. try { // longer sleep afterwards (not needed by GC, but may help with scheduling)
  527. Thread.sleep(200L);
  528. } catch (InterruptedException ie) {
  529. System.err.println("Interrupted while sleeping in serializers.BenchmarkRunner.doGc()");
  530. }
  531. }
  532. // ------------------------------------------------------------------------------------
  533. private static abstract class TestCase
  534. {
  535. public abstract <J> double run(Transformer<J,Object> transformer, Serializer<Object> serializer, J value, int iterations) throws Exception;
  536. }
  537. private static final class TestCaseRunner<J>
  538. {
  539. private final Transformer<J,Object> transformer;
  540. private final Serializer<Object> serializer;
  541. private final J value;
  542. public TestCaseRunner(Transformer<J,Object> transformer, Serializer<Object> serializer, J value)
  543. {
  544. this.transformer = transformer;
  545. this.serializer = serializer;
  546. this.value = value;
  547. }
  548. public double run(TestCase tc, int iterations) throws Exception
  549. {
  550. return tc.run(transformer, serializer, value, iterations);
  551. }
  552. public double runTakeMin(int trials, TestCase tc, int iterations) throws Exception
  553. {
  554. double minTime = Double.MAX_VALUE;
  555. for (int i = 0; i < trials; i++) {
  556. double time = tc.run(transformer, serializer, value, iterations);
  557. minTime = Math.min(minTime, time);
  558. }
  559. return minTime;
  560. }
  561. }
  562. enum measurements
  563. {
  564. timeCreate("create (nanos)"), timeSerializeDifferentObjects("ser (nanos)"), timeSerializeSameObject("ser+same (nanos)"),
  565. timeDeserializeNoFieldAccess("deser (nanos)"), timeDeserializeAndCheck("deser+deep (nanos)"), timeDeserializeAndCheckShallow("deser+shal (nanos)"),
  566. totalTime("total (nanos)"), length("size (bytes)"), lengthDeflate("size+dfl (bytes)"),
  567. ;
  568. public final String displayName;
  569. measurements(String displayName)
  570. {
  571. this.displayName = displayName;
  572. }
  573. }
  574. private static <J> EnumMap<measurements, Map<String, Double>>
  575. start(PrintWriter errors, int iterations, int trials, long warmupTime, boolean prewarm, Iterable<TestGroup.Entry<J,Object>> groups, J value) throws Exception
  576. {
  577. // Check correctness first.
  578. System.out.println("Checking correctness...");
  579. for (TestGroup.Entry<J,Object> entry : groups)
  580. {
  581. checkCorrectness(errors, entry.transformer, entry.serializer, value);
  582. }
  583. System.out.println("[done]");
  584. // Pre-warm.
  585. if (prewarm) {
  586. System.out.print("Pre-warmup...");
  587. for (TestGroup.Entry<J,Object> entry : groups)
  588. {
  589. TestCaseRunner<J> runner = new TestCaseRunner<J>(entry.transformer, entry.serializer, value);
  590. String name = entry.serializer.getName();
  591. System.out.print(" " + name);
  592. warmCreation(runner, warmupTime);
  593. warmSerialization(runner, warmupTime);
  594. warmDeserialization(runner, warmupTime);
  595. }
  596. System.out.println();
  597. System.out.println("[done]");
  598. }
  599. System.out.printf("%-32s %6s %7s %7s %7s %7s %7s %7s %6s %5s\n",
  600. "",
  601. "create",
  602. "ser",
  603. "+same",
  604. "deser",
  605. "+shal",
  606. "+deep",
  607. "total",
  608. "size",
  609. "+dfl");
  610. EnumMap<measurements, Map<String, Double>> values = new EnumMap<measurements, Map<String, Double>>(measurements.class);
  611. for (measurements m : measurements.values())
  612. values.put(m, new HashMap<String, Double>());
  613. // Actual tests.
  614. for (TestGroup.Entry<J,Object> entry : groups)
  615. {
  616. TestCaseRunner<J> runner = new TestCaseRunner<J>(entry.transformer, entry.serializer, value);
  617. String name = entry.serializer.getName();
  618. try {
  619. /*
  620. * Should only warm things for the serializer that we test next: HotSpot JIT will
  621. * otherwise spent most of its time optimizing slower ones... Use
  622. * -XX:CompileThreshold=1 to hint the JIT to start immediately
  623. *
  624. * Actually: 1 is often not a good value -- threshold is the number
  625. * of samples needed to trigger inlining, and there's no point in
  626. * inlining everything. Default value is in thousands, so lowering
  627. * it to, say, 1000 is usually better.
  628. */
  629. warmCreation(runner, warmupTime);
  630. doGc();
  631. double timeCreate = runner.runTakeMin(trials, Create, iterations * 100); // do more iteration for object creation because of its short time
  632. warmSerialization(runner, warmupTime);
  633. doGc();
  634. double timeSerializeDifferentObjects = runner.runTakeMin(trials, Serialize, iterations);
  635. doGc();
  636. double timeSerializeSameObject = runner.runTakeMin(trials, SerializeSameObject, iterations);
  637. warmDeserialization(runner, warmupTime);
  638. doGc();
  639. double timeDeserializeNoFieldAccess = runner.runTakeMin(trials, Deserialize, iterations);
  640. doGc();
  641. double timeDeserializeAndCheckShallow = runner.runTakeMin(trials, DeserializeAndCheckShallow, iterations);
  642. doGc();
  643. double timeDeserializeAndCheck = runner.runTakeMin(trials, DeserializeAndCheck, iterations);
  644. double totalTime = timeSerializeDifferentObjects + timeDeserializeAndCheck;
  645. byte[] array = entry.serializer.serialize(entry.transformer.forward(value));
  646. byte[] compressDeflate = compressDeflate(array);
  647. System.out.printf("%-32s %6.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %6d %5d\n",
  648. name,
  649. timeCreate,
  650. timeSerializeDifferentObjects,
  651. timeSerializeSameObject,
  652. timeDeserializeNoFieldAccess,
  653. timeDeserializeAndCheckShallow,
  654. timeDeserializeAndCheck,
  655. totalTime,
  656. array.length,
  657. compressDeflate.length);
  658. addValue(values, name, timeCreate, timeSerializeDifferentObjects, timeSerializeSameObject,
  659. timeDeserializeNoFieldAccess, timeDeserializeAndCheckShallow, timeDeserializeAndCheck, totalTime,
  660. array.length, compressDeflate.length);
  661. }
  662. catch (Exception ex) {
  663. System.out.println("ERROR: \"" + name + "\" crashed during benchmarking.");
  664. errors.println(ERROR_DIVIDER);
  665. errors.println("\"" + name + "\" crashed during benchmarking.");
  666. ex.printStackTrace(errors);
  667. }
  668. }
  669. return values;
  670. }
  671. private static byte[] compressDeflate(byte[] data)
  672. {
  673. try {
  674. ByteArrayOutputStream bout = new ByteArrayOutputStream(500);
  675. DeflaterOutputStream compresser = new DeflaterOutputStream(bout);
  676. compresser.write(data, 0, data.length);
  677. compresser.finish();
  678. compresser.flush();
  679. return bout.toByteArray();
  680. }
  681. catch (IOException ex) {
  682. AssertionError ae = new AssertionError("IOException while writing to ByteArrayOutputStream!");
  683. ae.initCause(ex);
  684. throw ae;
  685. }
  686. }
  687. /**
  688. * Method that tries to validate correctness of serializer, using
  689. * round-trip (construct, serializer, deserialize; compare objects
  690. * after steps 1 and 3).
  691. */
  692. private static <J> void checkCorrectness(PrintWriter errors, Transformer<J,Object> transformer, Serializer<Object> serializer, J value)
  693. throws Exception
  694. {
  695. Object specialInput;
  696. String name = serializer.getName();
  697. try {
  698. specialInput = transformer.forward(value);
  699. }
  700. catch (Exception ex) {
  701. System.out.println("ERROR: \"" + name + "\" crashed during forward transformation.");
  702. errors.println(ERROR_DIVIDER);
  703. errors.println("\"" + name + "\" crashed during forward transformation.");
  704. ex.printStackTrace(errors);
  705. return;
  706. }
  707. byte[] array;
  708. try {
  709. array = serializer.serialize(specialInput);
  710. }
  711. catch (Exception ex) {
  712. System.out.println("ERROR: \"" + name + "\" crashed during serialization.");
  713. errors.println(ERROR_DIVIDER);
  714. errors.println("\"" + name + "\" crashed during serialization.");
  715. ex.printStackTrace(errors);
  716. return;
  717. }
  718. Object specialOutput;
  719. try {
  720. specialOutput = serializer.deserialize(array);
  721. }
  722. catch (Exception ex) {
  723. System.out.println("ERROR: \"" + name + "\" crashed during deserialization.");
  724. errors.println(ERROR_DIVIDER);
  725. errors.println("\"" + name + "\" crashed during deserialization.");
  726. ex.printStackTrace(errors);
  727. return;
  728. }
  729. J output;
  730. try {
  731. output = transformer.reverse(specialOutput);
  732. }
  733. catch (Exception ex) {
  734. System.out.println("ERROR: \"" + name + "\" crashed during reverse transformation.");
  735. errors.println(ERROR_DIVIDER);
  736. errors.println("\"" + name + "\" crashed during reverse transformation.");
  737. ex.printStackTrace(errors);
  738. return;
  739. }
  740. if (!value.equals(output)) {
  741. System.out.println("ERROR: \"" + name + "\" failed round-trip check.");
  742. errors.println(ERROR_DIVIDER);
  743. errors.println("\"" + name + "\" failed round-trip check.");
  744. errors.println("ORIGINAL: " + value);
  745. errors.println("ROUNDTRIP: " + output);
  746. }
  747. }
  748. private static void printImages(EnumMap<measurements, Map<String, Double>> values)
  749. {
  750. for (measurements m : values.keySet()) {
  751. Map<String, Double> map = values.get(m);
  752. ArrayList<Map.Entry<String,Double>> list = new ArrayList<Map.Entry<String,Double>>(map.entrySet());
  753. Collections.sort(list, new Comparator<Map.Entry<String,Double>>() {
  754. public int compare (Map.Entry<String,Double> o1, Map.Entry<String,Double> o2) {
  755. double diff = o1.getValue() - o2.getValue();
  756. return diff > 0 ? 1 : (diff < 0 ? -1 : 0);
  757. }
  758. });
  759. LinkedHashMap<String, Double> sortedMap = new LinkedHashMap<String, Double>();
  760. for (Map.Entry<String, Double> entry : list) {
  761. if( !entry.getValue().isNaN() ) {
  762. sortedMap.put(entry.getKey(), entry.getValue());
  763. }
  764. }
  765. if (!sortedMap.isEmpty()) printImage(sortedMap, m);
  766. }
  767. }
  768. private static String urlEncode(String s)
  769. {
  770. try {
  771. return URLEncoder.encode(s, "UTF-8");
  772. }
  773. catch (UnsupportedEncodingException e) {
  774. throw new RuntimeException(e);
  775. }
  776. }
  777. private static void printImage(Map<String, Double> map, measurements m)
  778. {
  779. StringBuilder valSb = new StringBuilder();
  780. String names = "";
  781. double max = Double.MIN_NORMAL;
  782. for (Map.Entry<String, Double> entry : map.entrySet())
  783. {
  784. double value = entry.getValue();
  785. valSb.append((int) value).append(',');
  786. max = Math.max(max, entry.getValue());
  787. names = urlEncode(entry.getKey()) + '|' + names;
  788. }
  789. int headerSize = 30;
  790. int maxPixels = 300 * 1000; // Limit set by Google's Chart API.
  791. int width = 700;
  792. int maxHeight = maxPixels / width;
  793. int barThickness = 10;
  794. int barSpacing = 10;
  795. int height;
  796. // Reduce bar thickness and spacing until we can fit in the maximum height.
  797. while (true) {
  798. height = headerSize + map.size()*(barThickness + barSpacing);
  799. if (height <= maxHeight) break;
  800. barSpacing--;
  801. if (barSpacing == 1) break;
  802. height = headerSize + map.size()*(barThickness + barSpacing);
  803. if (height <= maxHeight) break;
  804. barThickness--;
  805. if (barThickness == 1) break;
  806. }
  807. boolean truncated = false;
  808. if (height > maxHeight) {
  809. truncated = true;
  810. height = maxHeight;
  811. }
  812. double scale = max * 1.1;
  813. System.out.println("<img src='https://chart.googleapis.com/chart?chtt="
  814. + urlEncode(m.displayName)
  815. + "&chf=c||lg||0||FFFFFF||1||76A4FB||0|bg||s||EFEFEF&chs="+width+"x"+height+"&chd=t:"
  816. + valSb.toString().substring(0, valSb.length() - 1)
  817. + "&chds=0,"+ scale
  818. + "&chxt=y"
  819. + "&chxl=0:|" + names.substring(0, names.length() - 1)
  820. + "&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'/>");
  821. if (truncated) {
  822. System.err.println("WARNING: Not enough room to fit all bars in chart.");
  823. }
  824. }
  825. private static void addValue(
  826. EnumMap<measurements, Map<String, Double>> values,
  827. String name,
  828. double timeCreate,
  829. double timeSerializeDifferentObjects,
  830. double timeSerializeSameObject,
  831. double timeDeserializeNoFieldAccess,
  832. double timeDeserializeAndCheckShallow,
  833. double timeDeserializeAndCheck,
  834. double totalTime,
  835. double length, double lengthDeflate)
  836. {
  837. values.get(measurements.timeSerializeDifferentObjects).put(name, timeSerializeDifferentObjects);
  838. values.get(measurements.timeSerializeSameObject).put(name, timeSerializeSameObject);
  839. values.get(measurements.timeDeserializeNoFieldAccess).put(name, timeDeserializeNoFieldAccess);
  840. values.get(measurements.timeDeserializeAndCheckShallow).put(name, timeDeserializeAndCheckShallow);
  841. values.get(measurements.timeDeserializeAndCheck).put(name, timeDeserializeAndCheck);
  842. values.get(measurements.totalTime).put(name, totalTime);
  843. values.get(measurements.length).put(name, length);
  844. values.get(measurements.lengthDeflate).put(name, lengthDeflate);
  845. values.get(measurements.timeCreate).put(name, timeCreate);
  846. }
  847. private static <J> void warmCreation(TestCaseRunner<J> runner, long warmupTime) throws Exception
  848. {
  849. // Instead of fixed counts, let's try to prime by running for N seconds
  850. long endTime = System.currentTimeMillis() + warmupTime;
  851. do
  852. {
  853. runner.run(Create, 10);
  854. }
  855. while (System.currentTimeMillis() < endTime);
  856. }
  857. private static <J> void warmSerialization(TestCaseRunner<J> runner, long warmupTime) throws Exception
  858. {
  859. // Instead of fixed counts, let's try to prime by running for N seconds
  860. long endTime = System.currentTimeMillis() + warmupTime;
  861. do
  862. {
  863. runner.run(Serialize, 10);
  864. }
  865. while (System.currentTimeMillis() < endTime);
  866. }
  867. private static <J> void warmDeserialization(TestCaseRunner<J> runner, long warmupTime) throws Exception
  868. {
  869. // Instead of fixed counts, let's try to prime by running for N seconds
  870. long endTime = System.currentTimeMillis() + warmupTime;
  871. do
  872. {
  873. runner.run(DeserializeAndCheck, 10);
  874. }
  875. while (System.currentTimeMillis() < endTime);
  876. }
  877. }