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