• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 // Protocol Buffers - Google's data interchange format
3 // Copyright 2019 Google Inc.  All rights reserved.
4 // https://github.com/protocolbuffers/protobuf
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 //     * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //     * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following disclaimer
14 // in the documentation and/or other materials provided with the
15 // distribution.
16 //     * Neither the name of Google Inc. nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #endregion
32 
33 using BenchmarkDotNet.Attributes;
34 using System;
35 using System.Buffers.Binary;
36 using System.Collections.Generic;
37 using System.IO;
38 using System.Buffers;
39 using System.Text;
40 
41 namespace Google.Protobuf.Benchmarks
42 {
43     /// <summary>
44     /// Benchmarks throughput when writing raw primitives.
45     /// </summary>
46     [MemoryDiagnoser]
47     public class WriteRawPrimitivesBenchmark
48     {
49         // key is the encodedSize of varint values
50         Dictionary<int, uint[]> varint32Values;
51         Dictionary<int, ulong[]> varint64Values;
52 
53         double[] doubleValues;
54         float[] floatValues;
55 
56         // key is the encodedSize of string values
57         Dictionary<int, string[]> stringValues;
58 
59         // key is the encodedSize of string values
60         Dictionary<int, string[]> nonAsciiStringValues;
61 
62         // key is the encodedSize of string values
63         Dictionary<int, ByteString[]> byteStringValues;
64 
65         // the buffer to which all the data will be written
66         byte[] outputBuffer;
67 
68         Random random = new Random(417384220);  // random but deterministic seed
69 
70         public IEnumerable<int> StringEncodedSizes => new[] { 1, 4, 10, 105, 10080 };
71 
72         public IEnumerable<int> NonAsciiStringEncodedSizes => new[] { 4, 10, 105, 10080 };
73 
74         [GlobalSetup]
GlobalSetup()75         public void GlobalSetup()
76         {
77             outputBuffer = new byte[BytesToWrite];
78 
79             varint32Values = new Dictionary<int, uint[]>();
80             varint64Values = new Dictionary<int, ulong[]>();
81             for (int encodedSize = 1; encodedSize <= 10; encodedSize++)
82             {
83                 if (encodedSize <= 5)
84                 {
85                     varint32Values.Add(encodedSize, CreateRandomVarints32(random, BytesToWrite / encodedSize, encodedSize));
86                 }
87                 varint64Values.Add(encodedSize, CreateRandomVarints64(random, BytesToWrite / encodedSize, encodedSize));
88             }
89 
90             doubleValues = CreateRandomDoubles(random, BytesToWrite / sizeof(double));
91             floatValues = CreateRandomFloats(random, BytesToWrite / sizeof(float));
92 
93             stringValues = new Dictionary<int, string[]>();
94 
95             byteStringValues = new Dictionary<int, ByteString[]>();
96             foreach(var encodedSize in StringEncodedSizes)
97             {
98                 stringValues.Add(encodedSize, CreateStrings(BytesToWrite / encodedSize, encodedSize));
99                 byteStringValues.Add(encodedSize, CreateByteStrings(BytesToWrite / encodedSize, encodedSize));
100             }
101 
102             nonAsciiStringValues = new Dictionary<int, string[]>();
103             foreach(var encodedSize in NonAsciiStringEncodedSizes)
104             {
105                 nonAsciiStringValues.Add(encodedSize, CreateNonAsciiStrings(BytesToWrite / encodedSize, encodedSize));
106             }
107         }
108 
109         // Total number of bytes that each benchmark will write.
110         // Measuring the time taken to write buffer of given size makes it easier to compare parsing speed for different
111         // types and makes it easy to calculate the througput (in MB/s)
112         // 10800 bytes is chosen because it is divisible by all possible encoded sizes for all primitive types {1..10}
113         [Params(10080)]
114         public int BytesToWrite { get; set; }
115 
116         [Benchmark]
117         [Arguments(1)]
118         [Arguments(2)]
119         [Arguments(3)]
120         [Arguments(4)]
121         [Arguments(5)]
WriteRawVarint32_CodedOutputStream(int encodedSize)122         public void WriteRawVarint32_CodedOutputStream(int encodedSize)
123         {
124             var values = varint32Values[encodedSize];
125             var cos = new CodedOutputStream(outputBuffer);
126             for (int i = 0; i < values.Length; i++)
127             {
128                 cos.WriteRawVarint32(values[i]);
129             }
130             cos.Flush();
131             cos.CheckNoSpaceLeft();
132         }
133 
134         [Benchmark]
135         [Arguments(1)]
136         [Arguments(2)]
137         [Arguments(3)]
138         [Arguments(4)]
139         [Arguments(5)]
WriteRawVarint32_WriteContext(int encodedSize)140         public void WriteRawVarint32_WriteContext(int encodedSize)
141         {
142             var values = varint32Values[encodedSize];
143             var span = new Span<byte>(outputBuffer);
144             WriteContext.Initialize(ref span, out WriteContext ctx);
145             for (int i = 0; i < values.Length; i++)
146             {
147                 ctx.WriteUInt32(values[i]);
148             }
149             ctx.Flush();
150             ctx.CheckNoSpaceLeft();
151         }
152 
153         [Benchmark]
154         [Arguments(1)]
155         [Arguments(2)]
156         [Arguments(3)]
157         [Arguments(4)]
158         [Arguments(5)]
159         [Arguments(6)]
160         [Arguments(7)]
161         [Arguments(8)]
162         [Arguments(9)]
163         [Arguments(10)]
WriteRawVarint64_CodedOutputStream(int encodedSize)164         public void WriteRawVarint64_CodedOutputStream(int encodedSize)
165         {
166             var values = varint64Values[encodedSize];
167             var cos = new CodedOutputStream(outputBuffer);
168             for (int i = 0; i < values.Length; i++)
169             {
170                 cos.WriteRawVarint64(values[i]);
171             }
172             cos.Flush();
173             cos.CheckNoSpaceLeft();
174         }
175 
176         [Benchmark]
177         [Arguments(1)]
178         [Arguments(2)]
179         [Arguments(3)]
180         [Arguments(4)]
181         [Arguments(5)]
182         [Arguments(6)]
183         [Arguments(7)]
184         [Arguments(8)]
185         [Arguments(9)]
186         [Arguments(10)]
WriteRawVarint64_WriteContext(int encodedSize)187         public void WriteRawVarint64_WriteContext(int encodedSize)
188         {
189             var values = varint64Values[encodedSize];
190             var span = new Span<byte>(outputBuffer);
191             WriteContext.Initialize(ref span, out WriteContext ctx);
192             for (int i = 0; i < values.Length; i++)
193             {
194                 ctx.WriteUInt64(values[i]);
195             }
196             ctx.Flush();
197             ctx.CheckNoSpaceLeft();
198         }
199 
200         [Benchmark]
WriteFixed32_CodedOutputStream()201         public void WriteFixed32_CodedOutputStream()
202         {
203             const int encodedSize = sizeof(uint);
204             var cos = new CodedOutputStream(outputBuffer);
205             for (int i = 0; i < BytesToWrite / encodedSize; i++)
206             {
207                 cos.WriteFixed32(12345);
208             }
209             cos.Flush();
210             cos.CheckNoSpaceLeft();
211         }
212 
213         [Benchmark]
WriteFixed32_WriteContext()214         public void WriteFixed32_WriteContext()
215         {
216             const int encodedSize = sizeof(uint);
217             var span = new Span<byte>(outputBuffer);
218             WriteContext.Initialize(ref span, out WriteContext ctx);
219             for (uint i = 0; i < BytesToWrite / encodedSize; i++)
220             {
221                 ctx.WriteFixed32(12345);
222             }
223             ctx.Flush();
224             ctx.CheckNoSpaceLeft();
225         }
226 
227         [Benchmark]
WriteFixed64_CodedOutputStream()228         public void WriteFixed64_CodedOutputStream()
229         {
230             const int encodedSize = sizeof(ulong);
231             var cos = new CodedOutputStream(outputBuffer);
232             for(int i = 0; i < BytesToWrite / encodedSize; i++)
233             {
234                 cos.WriteFixed64(123456789);
235             }
236             cos.Flush();
237             cos.CheckNoSpaceLeft();
238         }
239 
240         [Benchmark]
WriteFixed64_WriteContext()241         public void WriteFixed64_WriteContext()
242         {
243             const int encodedSize = sizeof(ulong);
244             var span = new Span<byte>(outputBuffer);
245             WriteContext.Initialize(ref span, out WriteContext ctx);
246             for (uint i = 0; i < BytesToWrite / encodedSize; i++)
247             {
248                 ctx.WriteFixed64(123456789);
249             }
250             ctx.Flush();
251             ctx.CheckNoSpaceLeft();
252         }
253 
254         [Benchmark]
WriteRawTag_OneByte_WriteContext()255         public void WriteRawTag_OneByte_WriteContext()
256         {
257             const int encodedSize = 1;
258             var span = new Span<byte>(outputBuffer);
259             WriteContext.Initialize(ref span, out WriteContext ctx);
260             for (uint i = 0; i < BytesToWrite / encodedSize; i++)
261             {
262                 ctx.WriteRawTag(16);
263             }
264             ctx.Flush();
265             ctx.CheckNoSpaceLeft();
266         }
267 
268         [Benchmark]
WriteRawTag_TwoBytes_WriteContext()269         public void WriteRawTag_TwoBytes_WriteContext()
270         {
271             const int encodedSize = 2;
272             var span = new Span<byte>(outputBuffer);
273             WriteContext.Initialize(ref span, out WriteContext ctx);
274             for (uint i = 0; i < BytesToWrite / encodedSize; i++)
275             {
276                 ctx.WriteRawTag(137, 6);
277             }
278             ctx.Flush();
279             ctx.CheckNoSpaceLeft();
280         }
281 
282         [Benchmark]
WriteRawTag_ThreeBytes_WriteContext()283         public void WriteRawTag_ThreeBytes_WriteContext()
284         {
285             const int encodedSize = 3;
286             var span = new Span<byte>(outputBuffer);
287             WriteContext.Initialize(ref span, out WriteContext ctx);
288             for (uint i = 0; i < BytesToWrite / encodedSize; i++)
289             {
290                 ctx.WriteRawTag(160, 131, 1);
291             }
292             ctx.Flush();
293             ctx.CheckNoSpaceLeft();
294         }
295 
296         [Benchmark]
Baseline_WriteContext()297         public void Baseline_WriteContext()
298         {
299             var span = new Span<byte>(outputBuffer);
300             WriteContext.Initialize(ref span, out WriteContext ctx);
301             ctx.state.position = outputBuffer.Length;
302             ctx.Flush();
303             ctx.CheckNoSpaceLeft();
304         }
305 
306         [Benchmark]
WriteRawFloat_CodedOutputStream()307         public void WriteRawFloat_CodedOutputStream()
308         {
309             var cos = new CodedOutputStream(outputBuffer);
310             foreach (var value in floatValues)
311             {
312                 cos.WriteFloat(value);
313             }
314             cos.Flush();
315             cos.CheckNoSpaceLeft();
316         }
317 
318         [Benchmark]
WriteRawFloat_WriteContext()319         public void WriteRawFloat_WriteContext()
320         {
321             var span = new Span<byte>(outputBuffer);
322             WriteContext.Initialize(ref span, out WriteContext ctx);
323             foreach (var value in floatValues)
324             {
325                 ctx.WriteFloat(value);
326             }
327             ctx.Flush();
328             ctx.CheckNoSpaceLeft();
329         }
330 
331         [Benchmark]
WriteRawDouble_CodedOutputStream()332         public void WriteRawDouble_CodedOutputStream()
333         {
334             var cos = new CodedOutputStream(outputBuffer);
335             foreach (var value in doubleValues)
336             {
337                 cos.WriteDouble(value);
338             }
339             cos.Flush();
340             cos.CheckNoSpaceLeft();
341         }
342 
343         [Benchmark]
WriteRawDouble_WriteContext()344         public void WriteRawDouble_WriteContext()
345         {
346             var span = new Span<byte>(outputBuffer);
347             WriteContext.Initialize(ref span, out WriteContext ctx);
348             foreach (var value in doubleValues)
349             {
350                 ctx.WriteDouble(value);
351             }
352             ctx.Flush();
353             ctx.CheckNoSpaceLeft();
354         }
355 
356         [Benchmark]
357         [ArgumentsSource(nameof(StringEncodedSizes))]
WriteString_CodedOutputStream(int encodedSize)358         public void WriteString_CodedOutputStream(int encodedSize)
359         {
360             var values = stringValues[encodedSize];
361             var cos = new CodedOutputStream(outputBuffer);
362             foreach (var value in values)
363             {
364                 cos.WriteString(value);
365             }
366             cos.Flush();
367             cos.CheckNoSpaceLeft();
368         }
369 
370         [Benchmark]
371         [ArgumentsSource(nameof(StringEncodedSizes))]
WriteString_WriteContext(int encodedSize)372         public void WriteString_WriteContext(int encodedSize)
373         {
374             var values = stringValues[encodedSize];
375             var span = new Span<byte>(outputBuffer);
376             WriteContext.Initialize(ref span, out WriteContext ctx);
377             foreach (var value in values)
378             {
379                 ctx.WriteString(value);
380             }
381             ctx.Flush();
382             ctx.CheckNoSpaceLeft();
383         }
384 
385         [Benchmark]
386         [ArgumentsSource(nameof(NonAsciiStringEncodedSizes))]
WriteNonAsciiString_CodedOutputStream(int encodedSize)387         public void WriteNonAsciiString_CodedOutputStream(int encodedSize)
388         {
389             var values = nonAsciiStringValues[encodedSize];
390             var cos = new CodedOutputStream(outputBuffer);
391             foreach (var value in values)
392             {
393                 cos.WriteString(value);
394             }
395             cos.Flush();
396             cos.CheckNoSpaceLeft();
397         }
398 
399         [Benchmark]
400         [ArgumentsSource(nameof(NonAsciiStringEncodedSizes))]
WriteNonAsciiString_WriteContext(int encodedSize)401         public void WriteNonAsciiString_WriteContext(int encodedSize)
402         {
403             var values = nonAsciiStringValues[encodedSize];
404             var span = new Span<byte>(outputBuffer);
405             WriteContext.Initialize(ref span, out WriteContext ctx);
406             foreach (var value in values)
407             {
408                 ctx.WriteString(value);
409             }
410             ctx.Flush();
411             ctx.CheckNoSpaceLeft();
412         }
413 
414         [Benchmark]
415         [ArgumentsSource(nameof(StringEncodedSizes))]
WriteBytes_CodedOutputStream(int encodedSize)416         public void WriteBytes_CodedOutputStream(int encodedSize)
417         {
418             var values = byteStringValues[encodedSize];
419             var cos = new CodedOutputStream(outputBuffer);
420             foreach (var value in values)
421             {
422                 cos.WriteBytes(value);
423             }
424             cos.Flush();
425             cos.CheckNoSpaceLeft();
426         }
427 
428         [Benchmark]
429         [ArgumentsSource(nameof(StringEncodedSizes))]
WriteBytes_WriteContext(int encodedSize)430         public void WriteBytes_WriteContext(int encodedSize)
431         {
432             var values = byteStringValues[encodedSize];
433             var span = new Span<byte>(outputBuffer);
434             WriteContext.Initialize(ref span, out WriteContext ctx);
435             foreach (var value in values)
436             {
437                 ctx.WriteBytes(value);
438             }
439             ctx.Flush();
440             ctx.CheckNoSpaceLeft();
441         }
442 
CreateRandomVarints32(Random random, int valueCount, int encodedSize)443         private static uint[] CreateRandomVarints32(Random random, int valueCount, int encodedSize)
444         {
445             var result = new uint[valueCount];
446             for (int i = 0; i < valueCount; i++)
447             {
448                 result[i] = (uint) ParseRawPrimitivesBenchmark.RandomUnsignedVarint(random, encodedSize, true);
449             }
450             return result;
451         }
452 
CreateRandomVarints64(Random random, int valueCount, int encodedSize)453         private static ulong[] CreateRandomVarints64(Random random, int valueCount, int encodedSize)
454         {
455             var result = new ulong[valueCount];
456             for (int i = 0; i < valueCount; i++)
457             {
458                 result[i] = ParseRawPrimitivesBenchmark.RandomUnsignedVarint(random, encodedSize, false);
459             }
460             return result;
461         }
462 
CreateRandomFloats(Random random, int valueCount)463         private static float[] CreateRandomFloats(Random random, int valueCount)
464         {
465             var result = new float[valueCount];
466             for (int i = 0; i < valueCount; i++)
467             {
468                 result[i] = (float)random.NextDouble();
469             }
470             return result;
471         }
472 
CreateRandomDoubles(Random random, int valueCount)473         private static double[] CreateRandomDoubles(Random random, int valueCount)
474         {
475             var result = new double[valueCount];
476             for (int i = 0; i < valueCount; i++)
477             {
478                 result[i] = random.NextDouble();
479             }
480             return result;
481         }
482 
CreateStrings(int valueCount, int encodedSize)483         private static string[] CreateStrings(int valueCount, int encodedSize)
484         {
485             var str = ParseRawPrimitivesBenchmark.CreateStringWithEncodedSize(encodedSize);
486 
487             var result = new string[valueCount];
488             for (int i = 0; i < valueCount; i++)
489             {
490                 result[i] = str;
491             }
492             return result;
493         }
494 
CreateNonAsciiStrings(int valueCount, int encodedSize)495         private static string[] CreateNonAsciiStrings(int valueCount, int encodedSize)
496         {
497             var str = ParseRawPrimitivesBenchmark.CreateNonAsciiStringWithEncodedSize(encodedSize);
498 
499             var result = new string[valueCount];
500             for (int i = 0; i < valueCount; i++)
501             {
502                 result[i] = str;
503             }
504             return result;
505         }
506 
CreateByteStrings(int valueCount, int encodedSize)507         private static ByteString[] CreateByteStrings(int valueCount, int encodedSize)
508         {
509             var str = ParseRawPrimitivesBenchmark.CreateStringWithEncodedSize(encodedSize);
510 
511             var result = new ByteString[valueCount];
512             for (int i = 0; i < valueCount; i++)
513             {
514                 result[i] = ByteString.CopyFrom(Encoding.UTF8.GetBytes(str));
515             }
516             return result;
517         }
518     }
519 }
520