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