blob: d0c581d4e97d754e1c72543484eaaece99ea8e51 [file] [log] [blame]
Jon Skeetad748532009-06-25 16:55:58 +01001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// http://github.com/jskeet/dotnet-protobufs/
4// Original C++/Java/Python code:
5// http://code.google.com/p/protobuf/
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
10//
11// * Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// * Redistributions in binary form must reproduce the above
14// copyright notice, this list of conditions and the following disclaimer
15// in the documentation and/or other materials provided with the
16// distribution.
17// * Neither the name of Google Inc. nor the names of its
18// contributors may be used to endorse or promote products derived from
19// this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32using System;
Jon Skeet8f8186a2009-01-16 10:57:40 +000033using System.Diagnostics;
34using System.IO;
Jon Skeet8f8186a2009-01-16 10:57:40 +000035
Jon Skeeta3767342009-01-16 18:06:56 +000036namespace Google.ProtocolBuffers.ProtoBench
37{
Jon Skeet8f8186a2009-01-16 10:57:40 +000038 /// <summary>
39 /// Simple benchmarking of arbitrary messages.
40 /// </summary>
41 public sealed class Program {
42
43 private static readonly TimeSpan MinSampleTime = TimeSpan.FromSeconds(2);
44 private static readonly TimeSpan TargetTime = TimeSpan.FromSeconds(30);
45
46 // Avoid a .NET 3.5 dependency
47 delegate void Action();
48
49 public static int Main(string[] args) {
Jon Skeet79c72a92009-01-16 13:19:31 +000050 if (args.Length < 2 || (args.Length % 2) != 0) {
51 Console.Error.WriteLine("Usage: ProtoBench <descriptor type name> <input data>");
Jon Skeet8f8186a2009-01-16 10:57:40 +000052 Console.Error.WriteLine("The descriptor type name is the fully-qualified message name,");
53 Console.Error.WriteLine("including assembly - e.g. Google.ProtocolBuffers.BenchmarkProtos.Message1,ProtoBench");
Jon Skeet79c72a92009-01-16 13:19:31 +000054 Console.Error.WriteLine("(You can specify multiple pairs of descriptor type name and input data.)");
Jon Skeet8f8186a2009-01-16 10:57:40 +000055 return 1;
56 }
Jon Skeet79c72a92009-01-16 13:19:31 +000057 bool success = true;
58 for (int i = 0; i < args.Length; i += 2) {
59 success &= RunTest(args[i], args[i + 1]);
60 }
61 return success ? 0 : 1;
62 }
63
64 /// <summary>
65 /// Runs a single test. Error messages are displayed to Console.Error, and the return value indicates
66 /// general success/failure.
67 /// </summary>
68 public static bool RunTest(string typeName, string file) {
69 Console.WriteLine("Benchmarking {0} with file {1}", typeName, file);
Jon Skeet8f8186a2009-01-16 10:57:40 +000070 IMessage defaultMessage;
71 try {
Jon Skeet79c72a92009-01-16 13:19:31 +000072 defaultMessage = MessageUtil.GetDefaultMessage(typeName);
Jon Skeet8f8186a2009-01-16 10:57:40 +000073 } catch (ArgumentException e) {
74 Console.Error.WriteLine(e.Message);
Jon Skeet79c72a92009-01-16 13:19:31 +000075 return false;
Jon Skeet8f8186a2009-01-16 10:57:40 +000076 }
77 try {
Jon Skeet79c72a92009-01-16 13:19:31 +000078 byte[] inputData = File.ReadAllBytes(file);
79 MemoryStream inputStream = new MemoryStream(inputData);
Jon Skeet8f8186a2009-01-16 10:57:40 +000080 ByteString inputString = ByteString.CopyFrom(inputData);
81 IMessage sampleMessage = defaultMessage.WeakCreateBuilderForType().WeakMergeFrom(inputString).WeakBuild();
Jon Skeet8f8186a2009-01-16 10:57:40 +000082 Benchmark("Serialize to byte string", inputData.Length, () => sampleMessage.ToByteString());
83 Benchmark("Serialize to byte array", inputData.Length, () => sampleMessage.ToByteArray());
84 Benchmark("Serialize to memory stream", inputData.Length, () => sampleMessage.WriteTo(new MemoryStream()));
Jon Skeeta3767342009-01-16 18:06:56 +000085 Benchmark("Deserialize from byte string", inputData.Length,
86 () => defaultMessage.WeakCreateBuilderForType()
87 .WeakMergeFrom(inputString)
88 .WeakBuild()
Jon Skeet79c72a92009-01-16 13:19:31 +000089 );
Jon Skeeta3767342009-01-16 18:06:56 +000090 Benchmark("Deserialize from byte array", inputData.Length,
91 () => defaultMessage.WeakCreateBuilderForType()
92 .WeakMergeFrom(CodedInputStream.CreateInstance(inputData))
93 .WeakBuild()
Jon Skeet8f8186a2009-01-16 10:57:40 +000094 );
Jon Skeeta3767342009-01-16 18:06:56 +000095 Benchmark("Deserialize from memory stream", inputData.Length, () => {
96 inputStream.Position = 0;
Jon Skeet8f8186a2009-01-16 10:57:40 +000097 defaultMessage.WeakCreateBuilderForType()
Jon Skeeta3767342009-01-16 18:06:56 +000098 .WeakMergeFrom(CodedInputStream.CreateInstance(inputStream))
99 .WeakBuild();
100 });
Jon Skeet75f42682009-03-05 14:22:28 +0000101 Console.WriteLine();
Jon Skeet79c72a92009-01-16 13:19:31 +0000102 return true;
Jon Skeet8f8186a2009-01-16 10:57:40 +0000103 } catch (Exception e) {
104 Console.Error.WriteLine("Error: {0}", e.Message);
105 Console.Error.WriteLine();
106 Console.Error.WriteLine("Detailed exception information: {0}", e);
Jon Skeet79c72a92009-01-16 13:19:31 +0000107 return false;
Jon Skeet8f8186a2009-01-16 10:57:40 +0000108 }
109 }
110
Jon Skeet79c72a92009-01-16 13:19:31 +0000111 private static void Benchmark(string name, long dataSize, Action action) {
Jon Skeet8f8186a2009-01-16 10:57:40 +0000112 // Make sure it's JITted
113 action();
114 // Run it progressively more times until we've got a reasonable sample
115
116 int iterations = 1;
117 TimeSpan elapsed = TimeAction(action, iterations);
118 while (elapsed < MinSampleTime) {
119 iterations *= 2;
120 elapsed = TimeAction(action, iterations);
121 }
122 // Upscale the sample to the target time. Do this in floating point arithmetic
123 // to avoid overflow issues.
124 iterations = (int) ((TargetTime.Ticks / (double)elapsed.Ticks) * iterations);
125 elapsed = TimeAction(action, iterations);
126 Console.WriteLine("{0}: {1} iterations in {2:f3}s; {3:f3}MB/s",
Jon Skeeta3767342009-01-16 18:06:56 +0000127 name, iterations, elapsed.TotalSeconds,
128 (iterations * dataSize) / (elapsed.TotalSeconds * 1024 * 1024));
Jon Skeet8f8186a2009-01-16 10:57:40 +0000129 }
130
131 private static TimeSpan TimeAction(Action action, int iterations) {
Jon Skeet75f42682009-03-05 14:22:28 +0000132 GC.Collect();
133 GC.WaitForPendingFinalizers();
Jon Skeet8f8186a2009-01-16 10:57:40 +0000134 Stopwatch sw = Stopwatch.StartNew();
135 for (int i = 0; i < iterations; i++) {
136 action();
137 }
138 sw.Stop();
139 return sw.Elapsed;
140 }
141 }
Jon Skeeta3767342009-01-16 18:06:56 +0000142}