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 33using BenchmarkDotNet.Attributes; 34using System; 35using System.Collections.Generic; 36using System.IO; 37using System.Linq; 38using System.Buffers; 39using Google.Protobuf.WellKnownTypes; 40using Benchmarks.Proto3; 41 42namespace 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] 60 public void GlobalSetup() 61 { 62 } 63 64 [Benchmark] 65 public IMessage ManyWrapperFieldsMessage_ParseFromByteArray() 66 { 67 return manyWrapperFieldsTest.ParseFromByteArray(); 68 } 69 70 [Benchmark] 71 public IMessage ManyWrapperFieldsMessage_ParseFromReadOnlySequence() 72 { 73 return manyWrapperFieldsTest.ParseFromReadOnlySequence(); 74 } 75 76 [Benchmark] 77 public IMessage ManyPrimitiveFieldsMessage_ParseFromByteArray() 78 { 79 return manyPrimitiveFieldsTest.ParseFromByteArray(); 80 } 81 82 [Benchmark] 83 public IMessage ManyPrimitiveFieldsMessage_ParseFromReadOnlySequence() 84 { 85 return manyPrimitiveFieldsTest.ParseFromReadOnlySequence(); 86 } 87 88 [Benchmark] 89 public IMessage RepeatedFieldMessage_ParseFromByteArray() 90 { 91 return repeatedFieldTest.ParseFromByteArray(); 92 } 93 94 [Benchmark] 95 public IMessage RepeatedFieldMessage_ParseFromReadOnlySequence() 96 { 97 return repeatedFieldTest.ParseFromReadOnlySequence(); 98 } 99 100 [Benchmark] 101 public IMessage EmptyMessage_ParseFromByteArray() 102 { 103 return emptyMessageTest.ParseFromByteArray(); 104 } 105 106 [Benchmark] 107 public IMessage EmptyMessage_ParseFromReadOnlySequence() 108 { 109 return emptyMessageTest.ParseFromReadOnlySequence(); 110 } 111 112 [Benchmark] 113 [ArgumentsSource(nameof(MessageCountValues))] 114 public void ManyWrapperFieldsMessage_ParseDelimitedMessagesFromByteArray(int messageCount) 115 { 116 manyWrapperFieldsTest.ParseDelimitedMessagesFromByteArray(messageCount); 117 } 118 119 [Benchmark] 120 [ArgumentsSource(nameof(MessageCountValues))] 121 public void ManyWrapperFieldsMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount) 122 { 123 manyWrapperFieldsTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount); 124 } 125 126 [Benchmark] 127 [ArgumentsSource(nameof(MessageCountValues))] 128 public void ManyPrimitiveFieldsMessage_ParseDelimitedMessagesFromByteArray(int messageCount) 129 { 130 manyPrimitiveFieldsTest.ParseDelimitedMessagesFromByteArray(messageCount); 131 } 132 133 [Benchmark] 134 [ArgumentsSource(nameof(MessageCountValues))] 135 public void ManyPrimitiveFieldsMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount) 136 { 137 manyPrimitiveFieldsTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount); 138 } 139 140 [Benchmark] 141 [ArgumentsSource(nameof(MessageCountValues))] 142 public void RepeatedFieldMessage_ParseDelimitedMessagesFromByteArray(int messageCount) 143 { 144 repeatedFieldTest.ParseDelimitedMessagesFromByteArray(messageCount); 145 } 146 147 [Benchmark] 148 [ArgumentsSource(nameof(MessageCountValues))] 149 public void RepeatedFieldMessage_ParseDelimitedMessagesFromReadOnlySequence(int messageCount) 150 { 151 repeatedFieldTest.ParseDelimitedMessagesFromReadOnlySequence(messageCount); 152 } 153 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 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 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 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 221 public IMessage ParseFromByteArray() => parser.ParseFrom(data); 222 223 public IMessage ParseFromReadOnlySequence() => parser.ParseFrom(dataSequence); 224 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 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 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