ProtoBench.java 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package com.google.protocolbuffers;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import java.io.RandomAccessFile;
  7. import java.lang.reflect.Method;
  8. import com.google.protobuf.ByteString;
  9. import com.google.protobuf.CodedInputStream;
  10. import com.google.protobuf.Message;
  11. public class ProtoBench {
  12. private static long MIN_SAMPLE_TIME_MS = 2 * 1000;
  13. private static long TARGET_TIME_MS = 30 * 1000;
  14. private ProtoBench() {
  15. // Prevent instantiation
  16. }
  17. public static void main(String[] args) {
  18. if (args.length < 2 || (args.length % 2) != 0) {
  19. System.err.println("Usage: ProtoBench <descriptor type name> <input data>");
  20. System.err.println("The descriptor type name is the fully-qualified message name,");
  21. System.err.println("e.g. com.google.protocolbuffers.benchmark.Message1");
  22. System.err.println("(You can specify multiple pairs of descriptor type name and input data.)");
  23. System.exit(1);
  24. }
  25. boolean success = true;
  26. for (int i = 0; i < args.length; i += 2) {
  27. success &= runTest(args[i], args[i + 1]);
  28. }
  29. System.exit(success ? 0 : 1);
  30. }
  31. /**
  32. * Runs a single test. Error messages are displayed to stderr, and the return value
  33. * indicates general success/failure.
  34. */
  35. public static boolean runTest(String type, String file) {
  36. System.out.println("Benchmarking " + type + " with file " + file);
  37. final Message defaultMessage;
  38. try {
  39. Class<?> clazz = Class.forName(type);
  40. Method method = clazz.getDeclaredMethod("getDefaultInstance");
  41. defaultMessage = (Message) method.invoke(null);
  42. } catch (Exception e) {
  43. // We want to do the same thing with all exceptions. Not generally nice,
  44. // but this is slightly different.
  45. System.err.println("Unable to get default message for " + type);
  46. return false;
  47. }
  48. try {
  49. final byte[] inputData = readAllBytes(file);
  50. final ByteArrayInputStream inputStream = new ByteArrayInputStream(inputData);
  51. final ByteString inputString = ByteString.copyFrom(inputData);
  52. final Message sampleMessage = defaultMessage.newBuilderForType().mergeFrom(inputString).build();
  53. benchmark("Serialize to byte string", inputData.length, new Action() {
  54. public void execute() { sampleMessage.toByteString(); }
  55. });
  56. benchmark("Serialize to byte array", inputData.length, new Action() {
  57. public void execute() { sampleMessage.toByteArray(); }
  58. });
  59. benchmark("Serialize to memory stream", inputData.length, new Action() {
  60. public void execute() throws IOException {
  61. sampleMessage.writeTo(new ByteArrayOutputStream());
  62. }
  63. });
  64. benchmark("Deserialize from byte string", inputData.length, new Action() {
  65. public void execute() throws IOException {
  66. defaultMessage.newBuilderForType().mergeFrom(inputString).build();
  67. }
  68. });
  69. benchmark("Deserialize from byte array", inputData.length, new Action() {
  70. public void execute() throws IOException {
  71. defaultMessage.newBuilderForType()
  72. .mergeFrom(CodedInputStream.newInstance(inputData)).build();
  73. }
  74. });
  75. benchmark("Deserialize from memory stream", inputData.length, new Action() {
  76. public void execute() throws IOException {
  77. defaultMessage.newBuilderForType()
  78. .mergeFrom(CodedInputStream.newInstance(inputStream)).build();
  79. inputStream.reset();
  80. }
  81. });
  82. System.out.println();
  83. return true;
  84. } catch (Exception e) {
  85. System.err.println("Error: " + e.getMessage());
  86. System.err.println("Detailed exception information:");
  87. e.printStackTrace(System.err);
  88. return false;
  89. }
  90. }
  91. private static void benchmark(String name, long dataSize, Action action) throws IOException {
  92. // Make sure it's JITted "reasonably" hard before running the first progress test
  93. for (int i=0; i < 100; i++) {
  94. action.execute();
  95. }
  96. // Run it progressively more times until we've got a reasonable sample
  97. int iterations = 1;
  98. long elapsed = timeAction(action, iterations);
  99. while (elapsed < MIN_SAMPLE_TIME_MS) {
  100. iterations *= 2;
  101. elapsed = timeAction(action, iterations);
  102. }
  103. // Upscale the sample to the target time. Do this in floating point arithmetic
  104. // to avoid overflow issues.
  105. iterations = (int) ((TARGET_TIME_MS / (double) elapsed) * iterations);
  106. elapsed = timeAction(action, iterations);
  107. System.out.println(name + ": " + iterations + " iterations in "
  108. + (elapsed/1000f) + "s; "
  109. + (iterations * dataSize) / (elapsed * 1024 * 1024 / 1000f)
  110. + "MB/s");
  111. }
  112. private static long timeAction(Action action, int iterations) throws IOException {
  113. long start = System.currentTimeMillis();
  114. for (int i = 0; i < iterations; i++) {
  115. action.execute();
  116. }
  117. long end = System.currentTimeMillis();
  118. return end - start;
  119. }
  120. private static byte[] readAllBytes(String filename) throws IOException {
  121. RandomAccessFile file = new RandomAccessFile(new File(filename), "r");
  122. byte[] content = new byte[(int) file.length()];
  123. file.readFully(content);
  124. return content;
  125. }
  126. /**
  127. * Interface used to capture a single action to benchmark.
  128. */
  129. interface Action {
  130. void execute() throws IOException;
  131. }
  132. }