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 using Benchmarks.Proto3; 41 42 namespace Google.Protobuf.Benchmarks 43 { 44 /// <summary> 45 /// Benchmark that tests parsing performance for various messages. 46 /// </summary> 47 [MemoryDiagnoser] 48 public class ParseMessagesBenchmark 49 { 50 const int MaxMessages = 100; 51 52 SubTest manyWrapperFieldsTest = new SubTest(CreateManyWrapperFieldsMessage(), ManyWrapperFieldsMessage.Parser, () => new ManyWrapperFieldsMessage(), MaxMessages); 53 SubTest manyPrimitiveFieldsTest = new SubTest(CreateManyPrimitiveFieldsMessage(), ManyPrimitiveFieldsMessage.Parser, () => new ManyPrimitiveFieldsMessage(), MaxMessages); 54 SubTest repeatedFieldTest = new SubTest(CreateRepeatedFieldMessage(), GoogleMessage1.Parser, () => new GoogleMessage1(), MaxMessages); 55 SubTest emptyMessageTest = new SubTest(new Empty(), Empty.Parser, () => new Empty(), MaxMessages); 56 57 public IEnumerable<int> MessageCountValues => new[] { 10, 100 }; 58 59 [GlobalSetup] GlobalSetup()60 public void GlobalSetup() 61 { 62 } 63 64 [Benchmark] ManyWrapperFieldsMessage_ParseFromByteArray()65 public IMessage ManyWrapperFieldsMessage_ParseFromByteArray() 66 { 67 return manyWrapperFieldsTest.ParseFromByteArray(); 68 } 69 70 [Benchmark] ManyWrapperFieldsMessage_ParseFromReadOnlySequence()71 public IMessage ManyWrapperFieldsMessage_ParseFromReadOnlySequence() 72 { 73 return manyWrapperFieldsTest.ParseFromReadOnlySequence(); 74 } 75 76 [Benchmark] ManyPrimitiveFieldsMessage_ParseFromByteArray()77 public IMessage ManyPrimitiveFieldsMessage_ParseFromByteArray() 78 { 79 return manyPrimitiveFieldsTest.ParseFromByteArray(); 80 } 81 82 [Benchmark] ManyPrimitiveFieldsMessage_ParseFromReadOnlySequence()83 public IMessage ManyPrimitiveFieldsMessage_ParseFromReadOnlySequence() 84 { 85 return manyPrimitiveFieldsTest.ParseFromReadOnlySequence(); 86 } 87 88 [Benchmark] RepeatedFieldMessage_ParseFromByteArray()89 public IMessage RepeatedFieldMessage_ParseFromByteArray() 90 { 91 return repeatedFieldTest.ParseFromByteArray(); 92 } 93 94 [Benchmark] RepeatedFieldMessage_ParseFromReadOnlySequence()95 public IMessage RepeatedFieldMessage_ParseFromReadOnlySequence() 96 { 97 return repeatedFieldTest.ParseFromReadOnlySequence(); 98 } 99 100 [Benchmark] EmptyMessage_ParseFromByteArray()101 public IMessage EmptyMessage_ParseFromByteArray() 102 { 103 return emptyMessageTest.ParseFromByteArray(); 104 } 105 106 [Benchmark] EmptyMessage_ParseFromReadOnlySequence()107 public IMessage EmptyMessage_ParseFromReadOnlySequence() 108 { 109 return emptyMessageTest.ParseFromReadOnlySequence(); 110 } 111 112 [Benchmark] 113 [ArgumentsSource(nameof(MessageCountValues))] ManyWrapperFieldsMessage_ParseDelimitedMessagesFromByteArray(int messageCount)114 public void ManyWrapperFieldsMessage_ParseDelimitedMessagesFromByteArray(int messageCount) 115 { 116 manyWrapperFieldsTest.ParseDelimitedMessagesFromByteArray(messageCount); 117 } 118 119 [Benchmark] 120 [ArgumentsSource(nameof(MessageCountValues))] ManyWrapperFieldsMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount)121 public void ManyWrapperFieldsMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount) 122 { 123 manyWrapperFieldsTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount); 124 } 125 126 [Benchmark] 127 [ArgumentsSource(nameof(MessageCountValues))] ManyPrimitiveFieldsMessage_ParseDelimitedMessagesFromByteArray(int messageCount)128 public void ManyPrimitiveFieldsMessage_ParseDelimitedMessagesFromByteArray(int messageCount) 129 { 130 manyPrimitiveFieldsTest.ParseDelimitedMessagesFromByteArray(messageCount); 131 } 132 133 [Benchmark] 134 [ArgumentsSource(nameof(MessageCountValues))] ManyPrimitiveFieldsMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount)135 public void ManyPrimitiveFieldsMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount) 136 { 137 manyPrimitiveFieldsTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount); 138 } 139 140 [Benchmark] 141 [ArgumentsSource(nameof(MessageCountValues))] RepeatedFieldMessage_ParseDelimitedMessagesFromByteArray(int messageCount)142 public void RepeatedFieldMessage_ParseDelimitedMessagesFromByteArray(int messageCount) 143 { 144 repeatedFieldTest.ParseDelimitedMessagesFromByteArray(messageCount); 145 } 146 147 [Benchmark] 148 [ArgumentsSource(nameof(MessageCountValues))] RepeatedFieldMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount)149 public void RepeatedFieldMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount) 150 { 151 repeatedFieldTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount); 152 } 153 CreateManyWrapperFieldsMessage()154 public static ManyWrapperFieldsMessage CreateManyWrapperFieldsMessage() 155 { 156 // Example data match data of an internal benchmarks 157 return new ManyWrapperFieldsMessage() 158 { 159 Int64Field19 = 123, 160 Int64Field37 = 1000032, 161 Int64Field26 = 3453524500, 162 DoubleField79 = 1.2, 163 DoubleField25 = 234, 164 DoubleField9 = 123.3, 165 DoubleField28 = 23, 166 DoubleField7 = 234, 167 DoubleField50 = 2.45 168 }; 169 } 170 CreateManyPrimitiveFieldsMessage()171 public static ManyPrimitiveFieldsMessage CreateManyPrimitiveFieldsMessage() 172 { 173 // Example data match data of an internal benchmarks 174 return new ManyPrimitiveFieldsMessage() 175 { 176 Int64Field19 = 123, 177 Int64Field37 = 1000032, 178 Int64Field26 = 3453524500, 179 DoubleField79 = 1.2, 180 DoubleField25 = 234, 181 DoubleField9 = 123.3, 182 DoubleField28 = 23, 183 DoubleField7 = 234, 184 DoubleField50 = 2.45 185 }; 186 } 187 CreateRepeatedFieldMessage()188 public static GoogleMessage1 CreateRepeatedFieldMessage() 189 { 190 // Message with a repeated fixed length item collection 191 var message = new GoogleMessage1(); 192 for (ulong i = 0; i < 1000; i++) 193 { 194 message.Field5.Add(i); 195 } 196 return message; 197 } 198 199 private class SubTest 200 { 201 private readonly IMessage message; 202 private readonly MessageParser parser; 203 private readonly Func<IMessage> factory; 204 private readonly byte[] data; 205 private readonly byte[] multipleMessagesData; 206 207 private ReadOnlySequence<byte> dataSequence; 208 private ReadOnlySequence<byte> multipleMessagesDataSequence; 209 SubTest(IMessage message, MessageParser parser, Func<IMessage> factory, int maxMessageCount)210 public SubTest(IMessage message, MessageParser parser, Func<IMessage> factory, int maxMessageCount) 211 { 212 this.message = message; 213 this.parser = parser; 214 this.factory = factory; 215 this.data = message.ToByteArray(); 216 this.multipleMessagesData = CreateBufferWithMultipleMessages(message, maxMessageCount); 217 this.dataSequence = new ReadOnlySequence<byte>(this.data); 218 this.multipleMessagesDataSequence = new ReadOnlySequence<byte>(this.multipleMessagesData); 219 } 220 ParseFromByteArray()221 public IMessage ParseFromByteArray() => parser.ParseFrom(data); 222 ParseFromReadOnlySequence()223 public IMessage ParseFromReadOnlySequence() => parser.ParseFrom(dataSequence); 224 ParseDelimitedMessagesFromByteArray(int messageCount)225 public void ParseDelimitedMessagesFromByteArray(int messageCount) 226 { 227 var input = new CodedInputStream(multipleMessagesData); 228 for (int i = 0; i < messageCount; i++) 229 { 230 var msg = factory(); 231 input.ReadMessage(msg); 232 } 233 } 234 ParseDelimitedMessagesFromReadOnlySequence(int messageCount)235 public void ParseDelimitedMessagesFromReadOnlySequence(int messageCount) 236 { 237 ParseContext.Initialize(multipleMessagesDataSequence, out ParseContext ctx); 238 for (int i = 0; i < messageCount; i++) 239 { 240 var msg = factory(); 241 ctx.ReadMessage(msg); 242 } 243 } 244 CreateBufferWithMultipleMessages(IMessage msg, int msgCount)245 private static byte[] CreateBufferWithMultipleMessages(IMessage msg, int msgCount) 246 { 247 var ms = new MemoryStream(); 248 var cos = new CodedOutputStream(ms); 249 for (int i = 0; i < msgCount; i++) 250 { 251 cos.WriteMessage(msg); 252 } 253 cos.Flush(); 254 return ms.ToArray(); 255 } 256 } 257 } 258 } 259