• 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.Collections.Generic;
36 using System.IO;
37 using System.Linq;
38 using System.Buffers;
39 using Google.Protobuf.WellKnownTypes;
40 
41 namespace Google.Protobuf.Benchmarks
42 {
43     /// <summary>
44     /// Benchmark that tests writing performance for various messages.
45     /// </summary>
46     [MemoryDiagnoser]
47     public class WriteMessagesBenchmark
48     {
49         const int MaxMessages = 100;
50 
51         SubTest manyWrapperFieldsTest = new SubTest(ParseMessagesBenchmark.CreateManyWrapperFieldsMessage(), MaxMessages);
52         SubTest manyPrimitiveFieldsTest = new SubTest(ParseMessagesBenchmark.CreateManyPrimitiveFieldsMessage(), MaxMessages);
53         SubTest emptyMessageTest = new SubTest(new Empty(), MaxMessages);
54 
55         public IEnumerable<int> MessageCountValues => new[] { 10, 100 };
56 
57         [GlobalSetup]
GlobalSetup()58         public void GlobalSetup()
59         {
60         }
61 
62         [Benchmark]
ManyWrapperFieldsMessage_ToByteArray()63         public byte[] ManyWrapperFieldsMessage_ToByteArray()
64         {
65             return manyWrapperFieldsTest.ToByteArray();
66         }
67 
68         [Benchmark]
ManyWrapperFieldsMessage_WriteToCodedOutputStream()69         public byte[] ManyWrapperFieldsMessage_WriteToCodedOutputStream()
70         {
71             return manyWrapperFieldsTest.WriteToCodedOutputStream_PreAllocatedBuffer();
72         }
73 
74         [Benchmark]
ManyWrapperFieldsMessage_WriteToSpan()75         public byte[] ManyWrapperFieldsMessage_WriteToSpan()
76         {
77             return manyWrapperFieldsTest.WriteToSpan_PreAllocatedBuffer();
78         }
79 
80 
81         [Benchmark]
ManyPrimitiveFieldsMessage_ToByteArray()82         public byte[] ManyPrimitiveFieldsMessage_ToByteArray()
83         {
84             return manyPrimitiveFieldsTest.ToByteArray();
85         }
86 
87         [Benchmark]
ManyPrimitiveFieldsMessage_WriteToCodedOutputStream()88         public byte[] ManyPrimitiveFieldsMessage_WriteToCodedOutputStream()
89         {
90             return manyPrimitiveFieldsTest.WriteToCodedOutputStream_PreAllocatedBuffer();
91         }
92 
93         [Benchmark]
ManyPrimitiveFieldsMessage_WriteToSpan()94         public byte[] ManyPrimitiveFieldsMessage_WriteToSpan()
95         {
96             return manyPrimitiveFieldsTest.WriteToSpan_PreAllocatedBuffer();
97         }
98 
99         [Benchmark]
EmptyMessage_ToByteArray()100         public byte[] EmptyMessage_ToByteArray()
101         {
102             return emptyMessageTest.ToByteArray();
103         }
104 
105         [Benchmark]
EmptyMessage_WriteToCodedOutputStream()106         public byte[] EmptyMessage_WriteToCodedOutputStream()
107         {
108             return emptyMessageTest.WriteToCodedOutputStream_PreAllocatedBuffer();
109         }
110 
111         [Benchmark]
EmptyMessage_WriteToSpan()112         public byte[] EmptyMessage_WriteToSpan()
113         {
114             return emptyMessageTest.WriteToSpan_PreAllocatedBuffer();
115         }
116 
117         [Benchmark]
118         [ArgumentsSource(nameof(MessageCountValues))]
ManyWrapperFieldsMessage_WriteDelimitedMessagesToCodedOutputStream(int messageCount)119         public void ManyWrapperFieldsMessage_WriteDelimitedMessagesToCodedOutputStream(int messageCount)
120         {
121             manyWrapperFieldsTest.WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(messageCount);
122         }
123 
124         [Benchmark]
125         [ArgumentsSource(nameof(MessageCountValues))]
ManyWrapperFieldsMessage_WriteDelimitedMessagesToSpan(int messageCount)126         public void ManyWrapperFieldsMessage_WriteDelimitedMessagesToSpan(int messageCount)
127         {
128             manyWrapperFieldsTest.WriteDelimitedMessagesToSpan_PreAllocatedBuffer(messageCount);
129         }
130 
131         [Benchmark]
132         [ArgumentsSource(nameof(MessageCountValues))]
ManyPrimitiveFieldsMessage_WriteDelimitedMessagesToCodedOutputStream(int messageCount)133         public void ManyPrimitiveFieldsMessage_WriteDelimitedMessagesToCodedOutputStream(int messageCount)
134         {
135             manyPrimitiveFieldsTest.WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(messageCount);
136         }
137 
138         [Benchmark]
139         [ArgumentsSource(nameof(MessageCountValues))]
ManyPrimitiveFieldsMessage_WriteDelimitedMessagesToSpan(int messageCount)140         public void ManyPrimitiveFieldsMessage_WriteDelimitedMessagesToSpan(int messageCount)
141         {
142             manyPrimitiveFieldsTest.WriteDelimitedMessagesToSpan_PreAllocatedBuffer(messageCount);
143         }
144 
145         private class SubTest
146         {
147             private readonly IMessage message;
148             private readonly byte[] outputBuffer;
149             private readonly byte[] multipleMessagesOutputBuffer;
150 
SubTest(IMessage message, int maxMessageCount)151             public SubTest(IMessage message, int maxMessageCount)
152             {
153                 this.message = message;
154 
155                 int messageSize = message.CalculateSize();
156                 this.outputBuffer = new byte[messageSize];
157                 this.multipleMessagesOutputBuffer = new byte[maxMessageCount * (messageSize + CodedOutputStream.ComputeLengthSize(messageSize))];
158             }
159 
ToByteArray()160             public byte[] ToByteArray() => message.ToByteArray();
161 
WriteToCodedOutputStream_PreAllocatedBuffer()162             public byte[] WriteToCodedOutputStream_PreAllocatedBuffer()
163             {
164                 var cos = new CodedOutputStream(outputBuffer);  // use pre-existing output buffer
165                 message.WriteTo(cos);
166                 return outputBuffer;
167             }
168 
WriteToSpan_PreAllocatedBuffer()169             public byte[] WriteToSpan_PreAllocatedBuffer()
170             {
171                 var span = new Span<byte>(outputBuffer);  // use pre-existing output buffer
172                 message.WriteTo(span);
173                 return outputBuffer;
174             }
175 
WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(int messageCount)176             public byte[] WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(int messageCount)
177             {
178                 var cos = new CodedOutputStream(multipleMessagesOutputBuffer);  // use pre-existing output buffer
179                 for (int i = 0; i < messageCount; i++)
180                 {
181                     cos.WriteMessage(message);
182                 }
183                 return multipleMessagesOutputBuffer;
184             }
185 
WriteDelimitedMessagesToSpan_PreAllocatedBuffer(int messageCount)186             public byte[] WriteDelimitedMessagesToSpan_PreAllocatedBuffer(int messageCount)
187             {
188                 var span = new Span<byte>(multipleMessagesOutputBuffer);  // use pre-existing output buffer
189                 WriteContext.Initialize(ref span, out WriteContext ctx);
190                 for (int i = 0; i < messageCount; i++)
191                 {
192                     ctx.WriteMessage(message);
193                 }
194                 return multipleMessagesOutputBuffer;
195             }
196         }
197     }
198 }
199