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; 40 41namespace 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] 58 public void GlobalSetup() 59 { 60 } 61 62 [Benchmark] 63 public byte[] ManyWrapperFieldsMessage_ToByteArray() 64 { 65 return manyWrapperFieldsTest.ToByteArray(); 66 } 67 68 [Benchmark] 69 public byte[] ManyWrapperFieldsMessage_WriteToCodedOutputStream() 70 { 71 return manyWrapperFieldsTest.WriteToCodedOutputStream_PreAllocatedBuffer(); 72 } 73 74 [Benchmark] 75 public byte[] ManyWrapperFieldsMessage_WriteToSpan() 76 { 77 return manyWrapperFieldsTest.WriteToSpan_PreAllocatedBuffer(); 78 } 79 80 81 [Benchmark] 82 public byte[] ManyPrimitiveFieldsMessage_ToByteArray() 83 { 84 return manyPrimitiveFieldsTest.ToByteArray(); 85 } 86 87 [Benchmark] 88 public byte[] ManyPrimitiveFieldsMessage_WriteToCodedOutputStream() 89 { 90 return manyPrimitiveFieldsTest.WriteToCodedOutputStream_PreAllocatedBuffer(); 91 } 92 93 [Benchmark] 94 public byte[] ManyPrimitiveFieldsMessage_WriteToSpan() 95 { 96 return manyPrimitiveFieldsTest.WriteToSpan_PreAllocatedBuffer(); 97 } 98 99 [Benchmark] 100 public byte[] EmptyMessage_ToByteArray() 101 { 102 return emptyMessageTest.ToByteArray(); 103 } 104 105 [Benchmark] 106 public byte[] EmptyMessage_WriteToCodedOutputStream() 107 { 108 return emptyMessageTest.WriteToCodedOutputStream_PreAllocatedBuffer(); 109 } 110 111 [Benchmark] 112 public byte[] EmptyMessage_WriteToSpan() 113 { 114 return emptyMessageTest.WriteToSpan_PreAllocatedBuffer(); 115 } 116 117 [Benchmark] 118 [ArgumentsSource(nameof(MessageCountValues))] 119 public void ManyWrapperFieldsMessage_WriteDelimitedMessagesToCodedOutputStream(int messageCount) 120 { 121 manyWrapperFieldsTest.WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(messageCount); 122 } 123 124 [Benchmark] 125 [ArgumentsSource(nameof(MessageCountValues))] 126 public void ManyWrapperFieldsMessage_WriteDelimitedMessagesToSpan(int messageCount) 127 { 128 manyWrapperFieldsTest.WriteDelimitedMessagesToSpan_PreAllocatedBuffer(messageCount); 129 } 130 131 [Benchmark] 132 [ArgumentsSource(nameof(MessageCountValues))] 133 public void ManyPrimitiveFieldsMessage_WriteDelimitedMessagesToCodedOutputStream(int messageCount) 134 { 135 manyPrimitiveFieldsTest.WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(messageCount); 136 } 137 138 [Benchmark] 139 [ArgumentsSource(nameof(MessageCountValues))] 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 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 160 public byte[] ToByteArray() => message.ToByteArray(); 161 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 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 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 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