blob: 2be2bf4f41545c3b3e6a77decb42ea8c1d34d32c [file] [log] [blame]
csharptest71f662c2011-05-20 15:15:34 -05001#region Copyright notice and license
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc. All rights reserved.
5// http://github.com/jskeet/dotnet-protobufs/
6// Original C++/Java/Python code:
7// http://code.google.com/p/protobuf/
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are
11// met:
12//
13// * Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15// * Redistributions in binary form must reproduce the above
16// copyright notice, this list of conditions and the following disclaimer
17// in the documentation and/or other materials provided with the
18// distribution.
19// * Neither the name of Google Inc. nor the names of its
20// contributors may be used to endorse or promote products derived from
21// this software without specific prior written permission.
22//
23// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
35#endregion
36
37using System;
csharptestc671a4b2011-06-08 11:51:24 -050038using System.Collections.Generic;
csharptest71f662c2011-05-20 15:15:34 -050039using System.Diagnostics;
40using System.IO;
csharptestc671a4b2011-06-08 11:51:24 -050041using System.Threading;
csharptest71f662c2011-05-20 15:15:34 -050042
43namespace Google.ProtocolBuffers.ProtoBench
44{
45 /// <summary>
46 /// Simple benchmarking of arbitrary messages.
47 /// </summary>
48 public sealed class Program
49 {
csharptestc671a4b2011-06-08 11:51:24 -050050 private static TimeSpan MinSampleTime = TimeSpan.FromSeconds(2);
51 private static TimeSpan TargetTime = TimeSpan.FromSeconds(30);
52 private static bool FastTest = false;
53 private static bool Verbose = false;
csharptest71f662c2011-05-20 15:15:34 -050054 // Avoid a .NET 3.5 dependency
55 private delegate void Action();
56
csharptestc671a4b2011-06-08 11:51:24 -050057 private delegate void BenchmarkTest(string name, long dataSize, Action action);
58
59 private static BenchmarkTest RunBenchmark;
60
csharptest920b09a2011-06-08 20:13:29 -050061 [STAThread]
csharptest71f662c2011-05-20 15:15:34 -050062 public static int Main(string[] args)
63 {
csharptestc671a4b2011-06-08 11:51:24 -050064 List<string> temp = new List<string>(args);
65
66 FastTest = temp.Remove("/fast") || temp.Remove("-fast");
67 Verbose = temp.Remove("/verbose") || temp.Remove("-verbose");
csharptest0e2d1442011-06-08 12:56:34 -050068
csharptestc671a4b2011-06-08 11:51:24 -050069 RunBenchmark = BenchmarkV1;
70 if (temp.Remove("/v2") || temp.Remove("-v2"))
csharptest0e2d1442011-06-08 12:56:34 -050071 {
72 string cpu = temp.Find(x => x.StartsWith("-cpu:"));
73 int cpuIx = 1;
74 if (cpu != null) cpuIx = 1 << Math.Max(0, int.Parse(cpu.Substring(5)));
75
76 //pin the entire process to a single CPU
77 Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(cpuIx);
78 Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
csharptestc671a4b2011-06-08 11:51:24 -050079 RunBenchmark = BenchmarkV2;
csharptest0e2d1442011-06-08 12:56:34 -050080 }
csharptestc671a4b2011-06-08 11:51:24 -050081 args = temp.ToArray();
82
csharptest71f662c2011-05-20 15:15:34 -050083 if (args.Length < 2 || (args.Length%2) != 0)
84 {
csharptestc671a4b2011-06-08 11:51:24 -050085 Console.Error.WriteLine("Usage: ProtoBench [/fast] <descriptor type name> <input data>");
csharptest71f662c2011-05-20 15:15:34 -050086 Console.Error.WriteLine("The descriptor type name is the fully-qualified message name,");
csharptestc671a4b2011-06-08 11:51:24 -050087 Console.Error.WriteLine("including assembly - e.g. Google.ProtocolBuffers.BenchmarkProtos.Message1,ProtoBench");
csharptest71f662c2011-05-20 15:15:34 -050088 Console.Error.WriteLine("(You can specify multiple pairs of descriptor type name and input data.)");
89 return 1;
90 }
csharptest2772dfe2011-06-08 15:50:58 -050091
csharptest71f662c2011-05-20 15:15:34 -050092 bool success = true;
93 for (int i = 0; i < args.Length; i += 2)
94 {
95 success &= RunTest(args[i], args[i + 1]);
96 }
97 return success ? 0 : 1;
98 }
csharptest2772dfe2011-06-08 15:50:58 -050099
csharptest71f662c2011-05-20 15:15:34 -0500100 /// <summary>
101 /// Runs a single test. Error messages are displayed to Console.Error, and the return value indicates
102 /// general success/failure.
103 /// </summary>
104 public static bool RunTest(string typeName, string file)
105 {
106 Console.WriteLine("Benchmarking {0} with file {1}", typeName, file);
107 IMessage defaultMessage;
108 try
109 {
110 defaultMessage = MessageUtil.GetDefaultMessage(typeName);
111 }
112 catch (ArgumentException e)
113 {
114 Console.Error.WriteLine(e.Message);
115 return false;
116 }
117 try
118 {
119 byte[] inputData = File.ReadAllBytes(file);
120 MemoryStream inputStream = new MemoryStream(inputData);
121 ByteString inputString = ByteString.CopyFrom(inputData);
122 IMessage sampleMessage =
123 defaultMessage.WeakCreateBuilderForType().WeakMergeFrom(inputString).WeakBuild();
csharptestc671a4b2011-06-08 11:51:24 -0500124 if(!FastTest) RunBenchmark("Serialize to byte string", inputData.Length, () => sampleMessage.ToByteString());
125 RunBenchmark("Serialize to byte array", inputData.Length, () => sampleMessage.ToByteArray());
126 if (!FastTest) RunBenchmark("Serialize to memory stream", inputData.Length,
csharptest71f662c2011-05-20 15:15:34 -0500127 () => sampleMessage.WriteTo(new MemoryStream()));
csharptestc671a4b2011-06-08 11:51:24 -0500128 if (!FastTest) RunBenchmark("Deserialize from byte string", inputData.Length,
csharptest71f662c2011-05-20 15:15:34 -0500129 () => defaultMessage.WeakCreateBuilderForType()
130 .WeakMergeFrom(inputString)
131 .WeakBuild()
132 );
csharptestc671a4b2011-06-08 11:51:24 -0500133 RunBenchmark("Deserialize from byte array", inputData.Length,
csharptest71f662c2011-05-20 15:15:34 -0500134 () => defaultMessage.WeakCreateBuilderForType()
135 .WeakMergeFrom(CodedInputStream.CreateInstance(inputData))
136 .WeakBuild()
137 );
csharptestc671a4b2011-06-08 11:51:24 -0500138 if (!FastTest) RunBenchmark("Deserialize from memory stream", inputData.Length,
139 () => {
140 inputStream.Position = 0;
141 defaultMessage.WeakCreateBuilderForType().WeakMergeFrom(
142 CodedInputStream.CreateInstance(inputStream))
143 .WeakBuild();
144 });
csharptest71f662c2011-05-20 15:15:34 -0500145 Console.WriteLine();
146 return true;
147 }
148 catch (Exception e)
149 {
150 Console.Error.WriteLine("Error: {0}", e.Message);
151 Console.Error.WriteLine();
152 Console.Error.WriteLine("Detailed exception information: {0}", e);
153 return false;
154 }
155 }
156
csharptestc671a4b2011-06-08 11:51:24 -0500157 private static void BenchmarkV2(string name, long dataSize, Action action)
158 {
159 TimeSpan elapsed = TimeSpan.Zero;
160 long runs = 0;
161 long totalCount = 0;
162 double best = double.MinValue, worst = double.MaxValue;
163
csharptest920b09a2011-06-08 20:13:29 -0500164 action();
165 // Run it progressively more times until we've got a reasonable sample
csharptestc671a4b2011-06-08 11:51:24 -0500166
csharptest920b09a2011-06-08 20:13:29 -0500167 int iterations = 100;
168 elapsed = TimeAction(action, iterations);
169 while (elapsed.TotalMilliseconds < 1000)
170 {
171 elapsed += TimeAction(action, iterations);
172 iterations *= 2;
173 }
csharptestc671a4b2011-06-08 11:51:24 -0500174
csharptest920b09a2011-06-08 20:13:29 -0500175 TimeSpan target = TimeSpan.FromSeconds(1);
csharptestc671a4b2011-06-08 11:51:24 -0500176
csharptest920b09a2011-06-08 20:13:29 -0500177 elapsed = TimeAction(action, iterations);
178 iterations = (int)((target.Ticks * iterations) / (double)elapsed.Ticks);
179 elapsed = TimeAction(action, iterations);
180 iterations = (int)((target.Ticks * iterations) / (double)elapsed.Ticks);
181 elapsed = TimeAction(action, iterations);
182 iterations = (int)((target.Ticks * iterations) / (double)elapsed.Ticks);
csharptestc671a4b2011-06-08 11:51:24 -0500183
csharptest920b09a2011-06-08 20:13:29 -0500184 double first = (iterations * dataSize) / (elapsed.TotalSeconds * 1024 * 1024);
185 if (Verbose) Console.WriteLine("Round ---: Count = {1,6}, Bps = {2,8:f3}", 0, iterations, first);
186 elapsed = TimeSpan.Zero;
187 int max = FastTest ? 10 : 30;
csharptestc671a4b2011-06-08 11:51:24 -0500188
csharptest920b09a2011-06-08 20:13:29 -0500189 while (runs < max)
190 {
191 TimeSpan cycle = TimeAction(action, iterations);
192 // Accumulate and scale for next cycle.
193
194 double bps = (iterations * dataSize) / (cycle.TotalSeconds * 1024 * 1024);
195 if (Verbose) Console.WriteLine("Round {0,3}: Count = {1,6}, Bps = {2,8:f3}", runs, iterations, bps);
csharptestc671a4b2011-06-08 11:51:24 -0500196
csharptest920b09a2011-06-08 20:13:29 -0500197 best = Math.Max(best, bps);
198 worst = Math.Min(worst, bps);
csharptestc671a4b2011-06-08 11:51:24 -0500199
csharptest920b09a2011-06-08 20:13:29 -0500200 runs++;
201 elapsed += cycle;
202 totalCount += iterations;
203 iterations = (int) ((target.Ticks*totalCount)/(double) elapsed.Ticks);
204 }
csharptestc671a4b2011-06-08 11:51:24 -0500205
206 Console.WriteLine("{0}: averages {1} per {2:f3}s for {3} runs; avg: {4:f3}mbps; best: {5:f3}mbps; worst: {6:f3}mbps",
207 name, totalCount / runs, elapsed.TotalSeconds / runs, runs,
208 (totalCount * dataSize) / (elapsed.TotalSeconds * 1024 * 1024), best, worst);
209 }
210
211 private static void BenchmarkV1(string name, long dataSize, Action action)
csharptest71f662c2011-05-20 15:15:34 -0500212 {
213 // Make sure it's JITted
214 action();
215 // Run it progressively more times until we've got a reasonable sample
216
217 int iterations = 1;
218 TimeSpan elapsed = TimeAction(action, iterations);
219 while (elapsed < MinSampleTime)
220 {
221 iterations *= 2;
222 elapsed = TimeAction(action, iterations);
223 }
224 // Upscale the sample to the target time. Do this in floating point arithmetic
225 // to avoid overflow issues.
226 iterations = (int) ((TargetTime.Ticks/(double) elapsed.Ticks)*iterations);
227 elapsed = TimeAction(action, iterations);
228 Console.WriteLine("{0}: {1} iterations in {2:f3}s; {3:f3}MB/s",
229 name, iterations, elapsed.TotalSeconds,
230 (iterations*dataSize)/(elapsed.TotalSeconds*1024*1024));
231 }
232
233 private static TimeSpan TimeAction(Action action, int iterations)
234 {
235 GC.Collect();
csharptestc671a4b2011-06-08 11:51:24 -0500236 GC.GetTotalMemory(true);
csharptest71f662c2011-05-20 15:15:34 -0500237 GC.WaitForPendingFinalizers();
csharptestc671a4b2011-06-08 11:51:24 -0500238
csharptest71f662c2011-05-20 15:15:34 -0500239 Stopwatch sw = Stopwatch.StartNew();
240 for (int i = 0; i < iterations; i++)
241 {
242 action();
243 }
244 sw.Stop();
245 return sw.Elapsed;
246 }
247 }
Jon Skeeta3767342009-01-16 18:06:56 +0000248}