blob: 7d91bbeddfc7069eee2839ad91aa70ed09729abe [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
csharptest71f662c2011-05-20 15:15:34 -050061 public static int Main(string[] args)
62 {
csharptestc671a4b2011-06-08 11:51:24 -050063 List<string> temp = new List<string>(args);
64
65 FastTest = temp.Remove("/fast") || temp.Remove("-fast");
66 Verbose = temp.Remove("/verbose") || temp.Remove("-verbose");
csharptest0e2d1442011-06-08 12:56:34 -050067
csharptestc671a4b2011-06-08 11:51:24 -050068 RunBenchmark = BenchmarkV1;
69 if (temp.Remove("/v2") || temp.Remove("-v2"))
csharptest0e2d1442011-06-08 12:56:34 -050070 {
71 string cpu = temp.Find(x => x.StartsWith("-cpu:"));
72 int cpuIx = 1;
73 if (cpu != null) cpuIx = 1 << Math.Max(0, int.Parse(cpu.Substring(5)));
74
75 //pin the entire process to a single CPU
76 Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(cpuIx);
77 Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
csharptestc671a4b2011-06-08 11:51:24 -050078 RunBenchmark = BenchmarkV2;
csharptest0e2d1442011-06-08 12:56:34 -050079 }
csharptestc671a4b2011-06-08 11:51:24 -050080 args = temp.ToArray();
81
csharptest71f662c2011-05-20 15:15:34 -050082 if (args.Length < 2 || (args.Length%2) != 0)
83 {
csharptestc671a4b2011-06-08 11:51:24 -050084 Console.Error.WriteLine("Usage: ProtoBench [/fast] <descriptor type name> <input data>");
csharptest71f662c2011-05-20 15:15:34 -050085 Console.Error.WriteLine("The descriptor type name is the fully-qualified message name,");
csharptestc671a4b2011-06-08 11:51:24 -050086 Console.Error.WriteLine("including assembly - e.g. Google.ProtocolBuffers.BenchmarkProtos.Message1,ProtoBench");
csharptest71f662c2011-05-20 15:15:34 -050087 Console.Error.WriteLine("(You can specify multiple pairs of descriptor type name and input data.)");
88 return 1;
89 }
90 bool success = true;
91 for (int i = 0; i < args.Length; i += 2)
92 {
93 success &= RunTest(args[i], args[i + 1]);
94 }
95 return success ? 0 : 1;
96 }
97
98 /// <summary>
99 /// Runs a single test. Error messages are displayed to Console.Error, and the return value indicates
100 /// general success/failure.
101 /// </summary>
102 public static bool RunTest(string typeName, string file)
103 {
104 Console.WriteLine("Benchmarking {0} with file {1}", typeName, file);
105 IMessage defaultMessage;
106 try
107 {
108 defaultMessage = MessageUtil.GetDefaultMessage(typeName);
109 }
110 catch (ArgumentException e)
111 {
112 Console.Error.WriteLine(e.Message);
113 return false;
114 }
115 try
116 {
117 byte[] inputData = File.ReadAllBytes(file);
118 MemoryStream inputStream = new MemoryStream(inputData);
119 ByteString inputString = ByteString.CopyFrom(inputData);
120 IMessage sampleMessage =
121 defaultMessage.WeakCreateBuilderForType().WeakMergeFrom(inputString).WeakBuild();
csharptestc671a4b2011-06-08 11:51:24 -0500122 if(!FastTest) RunBenchmark("Serialize to byte string", inputData.Length, () => sampleMessage.ToByteString());
123 RunBenchmark("Serialize to byte array", inputData.Length, () => sampleMessage.ToByteArray());
124 if (!FastTest) RunBenchmark("Serialize to memory stream", inputData.Length,
csharptest71f662c2011-05-20 15:15:34 -0500125 () => sampleMessage.WriteTo(new MemoryStream()));
csharptestc671a4b2011-06-08 11:51:24 -0500126 if (!FastTest) RunBenchmark("Deserialize from byte string", inputData.Length,
csharptest71f662c2011-05-20 15:15:34 -0500127 () => defaultMessage.WeakCreateBuilderForType()
128 .WeakMergeFrom(inputString)
129 .WeakBuild()
130 );
csharptestc671a4b2011-06-08 11:51:24 -0500131 RunBenchmark("Deserialize from byte array", inputData.Length,
csharptest71f662c2011-05-20 15:15:34 -0500132 () => defaultMessage.WeakCreateBuilderForType()
133 .WeakMergeFrom(CodedInputStream.CreateInstance(inputData))
134 .WeakBuild()
135 );
csharptestc671a4b2011-06-08 11:51:24 -0500136 if (!FastTest) RunBenchmark("Deserialize from memory stream", inputData.Length,
137 () => {
138 inputStream.Position = 0;
139 defaultMessage.WeakCreateBuilderForType().WeakMergeFrom(
140 CodedInputStream.CreateInstance(inputStream))
141 .WeakBuild();
142 });
csharptest71f662c2011-05-20 15:15:34 -0500143 Console.WriteLine();
144 return true;
145 }
146 catch (Exception e)
147 {
148 Console.Error.WriteLine("Error: {0}", e.Message);
149 Console.Error.WriteLine();
150 Console.Error.WriteLine("Detailed exception information: {0}", e);
151 return false;
152 }
153 }
154
csharptestc671a4b2011-06-08 11:51:24 -0500155 private static void BenchmarkV2(string name, long dataSize, Action action)
156 {
157 TimeSpan elapsed = TimeSpan.Zero;
158 long runs = 0;
159 long totalCount = 0;
160 double best = double.MinValue, worst = double.MaxValue;
161
162 ThreadStart threadProc =
163 delegate()
164 {
165 action();
166 // Run it progressively more times until we've got a reasonable sample
167
168 int iterations = 100;
169 elapsed = TimeAction(action, iterations);
170 while (elapsed.TotalMilliseconds < 1000)
171 {
172 elapsed += TimeAction(action, iterations);
173 iterations *= 2;
174 }
175
176 TimeSpan target = TimeSpan.FromSeconds(1);
177
178 elapsed = TimeAction(action, iterations);
179 iterations = (int)((target.Ticks * iterations) / (double)elapsed.Ticks);
180 elapsed = TimeAction(action, iterations);
181 iterations = (int)((target.Ticks * iterations) / (double)elapsed.Ticks);
182 elapsed = TimeAction(action, iterations);
183 iterations = (int)((target.Ticks * iterations) / (double)elapsed.Ticks);
184
185 double first = (iterations * dataSize) / (elapsed.TotalSeconds * 1024 * 1024);
186 if (Verbose) Console.WriteLine("Round ---: Count = {1,6}, Bps = {2,8:f3}", 0, iterations, first);
187 elapsed = TimeSpan.Zero;
188 int max = FastTest ? 30 : 100;
189
190 while (runs < max)
191 {
192 TimeSpan cycle = TimeAction(action, iterations);
193 // Accumulate and scale for next cycle.
194
195 double bps = (iterations * dataSize) / (cycle.TotalSeconds * 1024 * 1024);
196 if (Verbose) Console.WriteLine("Round {0,3}: Count = {1,6}, Bps = {2,8:f3}", runs, iterations, bps);
csharptestc671a4b2011-06-08 11:51:24 -0500197
198 best = Math.Max(best, bps);
199 worst = Math.Min(worst, bps);
200
201 runs++;
202 elapsed += cycle;
203 totalCount += iterations;
204 iterations = (int) ((target.Ticks*totalCount)/(double) elapsed.Ticks);
205 }
206 };
207
208 Thread work = new Thread(threadProc);
209 work.Name = "Worker";
210 work.Priority = ThreadPriority.Highest;
211 work.SetApartmentState(ApartmentState.STA);
212 work.Start();
213 work.Join();
214
215 Console.WriteLine("{0}: averages {1} per {2:f3}s for {3} runs; avg: {4:f3}mbps; best: {5:f3}mbps; worst: {6:f3}mbps",
216 name, totalCount / runs, elapsed.TotalSeconds / runs, runs,
217 (totalCount * dataSize) / (elapsed.TotalSeconds * 1024 * 1024), best, worst);
218 }
219
220 private static void BenchmarkV1(string name, long dataSize, Action action)
csharptest71f662c2011-05-20 15:15:34 -0500221 {
222 // Make sure it's JITted
223 action();
224 // Run it progressively more times until we've got a reasonable sample
225
226 int iterations = 1;
227 TimeSpan elapsed = TimeAction(action, iterations);
228 while (elapsed < MinSampleTime)
229 {
230 iterations *= 2;
231 elapsed = TimeAction(action, iterations);
232 }
233 // Upscale the sample to the target time. Do this in floating point arithmetic
234 // to avoid overflow issues.
235 iterations = (int) ((TargetTime.Ticks/(double) elapsed.Ticks)*iterations);
236 elapsed = TimeAction(action, iterations);
237 Console.WriteLine("{0}: {1} iterations in {2:f3}s; {3:f3}MB/s",
238 name, iterations, elapsed.TotalSeconds,
239 (iterations*dataSize)/(elapsed.TotalSeconds*1024*1024));
240 }
241
242 private static TimeSpan TimeAction(Action action, int iterations)
243 {
244 GC.Collect();
csharptestc671a4b2011-06-08 11:51:24 -0500245 GC.GetTotalMemory(true);
csharptest71f662c2011-05-20 15:15:34 -0500246 GC.WaitForPendingFinalizers();
csharptestc671a4b2011-06-08 11:51:24 -0500247
csharptest71f662c2011-05-20 15:15:34 -0500248 Stopwatch sw = Stopwatch.StartNew();
249 for (int i = 0; i < iterations; i++)
250 {
251 action();
252 }
253 sw.Stop();
254 return sw.Elapsed;
255 }
256 }
Jon Skeeta3767342009-01-16 18:06:56 +0000257}