1*1b3f573fSAndroid Build Coastguard Worker #region Copyright notice and license 2*1b3f573fSAndroid Build Coastguard Worker // Protocol Buffers - Google's data interchange format 3*1b3f573fSAndroid Build Coastguard Worker // Copyright 2019 Google Inc. All rights reserved. 4*1b3f573fSAndroid Build Coastguard Worker // https://github.com/protocolbuffers/protobuf 5*1b3f573fSAndroid Build Coastguard Worker // 6*1b3f573fSAndroid Build Coastguard Worker // Redistribution and use in source and binary forms, with or without 7*1b3f573fSAndroid Build Coastguard Worker // modification, are permitted provided that the following conditions are 8*1b3f573fSAndroid Build Coastguard Worker // met: 9*1b3f573fSAndroid Build Coastguard Worker // 10*1b3f573fSAndroid Build Coastguard Worker // * Redistributions of source code must retain the above copyright 11*1b3f573fSAndroid Build Coastguard Worker // notice, this list of conditions and the following disclaimer. 12*1b3f573fSAndroid Build Coastguard Worker // * Redistributions in binary form must reproduce the above 13*1b3f573fSAndroid Build Coastguard Worker // copyright notice, this list of conditions and the following disclaimer 14*1b3f573fSAndroid Build Coastguard Worker // in the documentation and/or other materials provided with the 15*1b3f573fSAndroid Build Coastguard Worker // distribution. 16*1b3f573fSAndroid Build Coastguard Worker // * Neither the name of Google Inc. nor the names of its 17*1b3f573fSAndroid Build Coastguard Worker // contributors may be used to endorse or promote products derived from 18*1b3f573fSAndroid Build Coastguard Worker // this software without specific prior written permission. 19*1b3f573fSAndroid Build Coastguard Worker // 20*1b3f573fSAndroid Build Coastguard Worker // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21*1b3f573fSAndroid Build Coastguard Worker // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23*1b3f573fSAndroid Build Coastguard Worker // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24*1b3f573fSAndroid Build Coastguard Worker // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25*1b3f573fSAndroid Build Coastguard Worker // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27*1b3f573fSAndroid Build Coastguard Worker // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28*1b3f573fSAndroid Build Coastguard Worker // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29*1b3f573fSAndroid Build Coastguard Worker // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30*1b3f573fSAndroid Build Coastguard Worker // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31*1b3f573fSAndroid Build Coastguard Worker #endregion 32*1b3f573fSAndroid Build Coastguard Worker 33*1b3f573fSAndroid Build Coastguard Worker using BenchmarkDotNet.Attributes; 34*1b3f573fSAndroid Build Coastguard Worker using System; 35*1b3f573fSAndroid Build Coastguard Worker using System.Buffers.Binary; 36*1b3f573fSAndroid Build Coastguard Worker using System.Collections.Generic; 37*1b3f573fSAndroid Build Coastguard Worker using System.IO; 38*1b3f573fSAndroid Build Coastguard Worker using System.Buffers; 39*1b3f573fSAndroid Build Coastguard Worker 40*1b3f573fSAndroid Build Coastguard Worker namespace Google.Protobuf.Benchmarks 41*1b3f573fSAndroid Build Coastguard Worker { 42*1b3f573fSAndroid Build Coastguard Worker /// <summary> 43*1b3f573fSAndroid Build Coastguard Worker /// Benchmarks throughput when parsing raw primitives. 44*1b3f573fSAndroid Build Coastguard Worker /// </summary> 45*1b3f573fSAndroid Build Coastguard Worker [MemoryDiagnoser] 46*1b3f573fSAndroid Build Coastguard Worker public class ParseRawPrimitivesBenchmark 47*1b3f573fSAndroid Build Coastguard Worker { 48*1b3f573fSAndroid Build Coastguard Worker // key is the encodedSize of varint values 49*1b3f573fSAndroid Build Coastguard Worker Dictionary<int, byte[]> varintInputBuffers; 50*1b3f573fSAndroid Build Coastguard Worker 51*1b3f573fSAndroid Build Coastguard Worker byte[] doubleInputBuffer; 52*1b3f573fSAndroid Build Coastguard Worker byte[] floatInputBuffer; 53*1b3f573fSAndroid Build Coastguard Worker byte[] fixedIntInputBuffer; 54*1b3f573fSAndroid Build Coastguard Worker 55*1b3f573fSAndroid Build Coastguard Worker // key is the encodedSize of string values 56*1b3f573fSAndroid Build Coastguard Worker Dictionary<int, byte[]> stringInputBuffers; 57*1b3f573fSAndroid Build Coastguard Worker Dictionary<int, ReadOnlySequence<byte>> stringInputBuffersSegmented; 58*1b3f573fSAndroid Build Coastguard Worker 59*1b3f573fSAndroid Build Coastguard Worker Random random = new Random(417384220); // random but deterministic seed 60*1b3f573fSAndroid Build Coastguard Worker 61*1b3f573fSAndroid Build Coastguard Worker public IEnumerable<int> StringEncodedSizes => new[] { 1, 4, 10, 105, 10080 }; 62*1b3f573fSAndroid Build Coastguard Worker public IEnumerable<int> StringSegmentedEncodedSizes => new[] { 105, 10080 }; 63*1b3f573fSAndroid Build Coastguard Worker 64*1b3f573fSAndroid Build Coastguard Worker [GlobalSetup] GlobalSetup()65*1b3f573fSAndroid Build Coastguard Worker public void GlobalSetup() 66*1b3f573fSAndroid Build Coastguard Worker { 67*1b3f573fSAndroid Build Coastguard Worker // add some extra values that we won't read just to make sure we are far enough from the end of the buffer 68*1b3f573fSAndroid Build Coastguard Worker // which allows the parser fastpath to always kick in. 69*1b3f573fSAndroid Build Coastguard Worker const int paddingValueCount = 100; 70*1b3f573fSAndroid Build Coastguard Worker 71*1b3f573fSAndroid Build Coastguard Worker varintInputBuffers = new Dictionary<int, byte[]>(); 72*1b3f573fSAndroid Build Coastguard Worker for (int encodedSize = 1; encodedSize <= 10; encodedSize++) 73*1b3f573fSAndroid Build Coastguard Worker { 74*1b3f573fSAndroid Build Coastguard Worker byte[] buffer = CreateBufferWithRandomVarints(random, BytesToParse / encodedSize, encodedSize, paddingValueCount); 75*1b3f573fSAndroid Build Coastguard Worker varintInputBuffers.Add(encodedSize, buffer); 76*1b3f573fSAndroid Build Coastguard Worker } 77*1b3f573fSAndroid Build Coastguard Worker 78*1b3f573fSAndroid Build Coastguard Worker doubleInputBuffer = CreateBufferWithRandomDoubles(random, BytesToParse / sizeof(double), paddingValueCount); 79*1b3f573fSAndroid Build Coastguard Worker floatInputBuffer = CreateBufferWithRandomFloats(random, BytesToParse / sizeof(float), paddingValueCount); 80*1b3f573fSAndroid Build Coastguard Worker fixedIntInputBuffer = CreateBufferWithRandomData(random, BytesToParse / sizeof(long), sizeof(long), paddingValueCount); 81*1b3f573fSAndroid Build Coastguard Worker 82*1b3f573fSAndroid Build Coastguard Worker stringInputBuffers = new Dictionary<int, byte[]>(); 83*1b3f573fSAndroid Build Coastguard Worker foreach (var encodedSize in StringEncodedSizes) 84*1b3f573fSAndroid Build Coastguard Worker { 85*1b3f573fSAndroid Build Coastguard Worker byte[] buffer = CreateBufferWithStrings(BytesToParse / encodedSize, encodedSize, encodedSize < 10 ? 10 : 1 ); 86*1b3f573fSAndroid Build Coastguard Worker stringInputBuffers.Add(encodedSize, buffer); 87*1b3f573fSAndroid Build Coastguard Worker } 88*1b3f573fSAndroid Build Coastguard Worker 89*1b3f573fSAndroid Build Coastguard Worker stringInputBuffersSegmented = new Dictionary<int, ReadOnlySequence<byte>>(); 90*1b3f573fSAndroid Build Coastguard Worker foreach (var encodedSize in StringSegmentedEncodedSizes) 91*1b3f573fSAndroid Build Coastguard Worker { 92*1b3f573fSAndroid Build Coastguard Worker byte[] buffer = CreateBufferWithStrings(BytesToParse / encodedSize, encodedSize, encodedSize < 10 ? 10 : 1); 93*1b3f573fSAndroid Build Coastguard Worker stringInputBuffersSegmented.Add(encodedSize, ReadOnlySequenceFactory.CreateWithContent(buffer, segmentSize: 128, addEmptySegmentDelimiters: false)); 94*1b3f573fSAndroid Build Coastguard Worker } 95*1b3f573fSAndroid Build Coastguard Worker } 96*1b3f573fSAndroid Build Coastguard Worker 97*1b3f573fSAndroid Build Coastguard Worker // Total number of bytes that each benchmark will parse. 98*1b3f573fSAndroid Build Coastguard Worker // Measuring the time taken to parse buffer of given size makes it easier to compare parsing speed for different 99*1b3f573fSAndroid Build Coastguard Worker // types and makes it easy to calculate the througput (in MB/s) 100*1b3f573fSAndroid Build Coastguard Worker // 10800 bytes is chosen because it is divisible by all possible encoded sizes for all primitive types {1..10} 101*1b3f573fSAndroid Build Coastguard Worker [Params(10080)] 102*1b3f573fSAndroid Build Coastguard Worker public int BytesToParse { get; set; } 103*1b3f573fSAndroid Build Coastguard Worker 104*1b3f573fSAndroid Build Coastguard Worker [Benchmark] 105*1b3f573fSAndroid Build Coastguard Worker [Arguments(1)] 106*1b3f573fSAndroid Build Coastguard Worker [Arguments(2)] 107*1b3f573fSAndroid Build Coastguard Worker [Arguments(3)] 108*1b3f573fSAndroid Build Coastguard Worker [Arguments(4)] 109*1b3f573fSAndroid Build Coastguard Worker [Arguments(5)] ParseRawVarint32_CodedInputStream(int encodedSize)110*1b3f573fSAndroid Build Coastguard Worker public int ParseRawVarint32_CodedInputStream(int encodedSize) 111*1b3f573fSAndroid Build Coastguard Worker { 112*1b3f573fSAndroid Build Coastguard Worker CodedInputStream cis = new CodedInputStream(varintInputBuffers[encodedSize]); 113*1b3f573fSAndroid Build Coastguard Worker int sum = 0; 114*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 115*1b3f573fSAndroid Build Coastguard Worker { 116*1b3f573fSAndroid Build Coastguard Worker sum += cis.ReadInt32(); 117*1b3f573fSAndroid Build Coastguard Worker } 118*1b3f573fSAndroid Build Coastguard Worker return sum; 119*1b3f573fSAndroid Build Coastguard Worker } 120*1b3f573fSAndroid Build Coastguard Worker 121*1b3f573fSAndroid Build Coastguard Worker [Benchmark] 122*1b3f573fSAndroid Build Coastguard Worker [Arguments(1)] 123*1b3f573fSAndroid Build Coastguard Worker [Arguments(2)] 124*1b3f573fSAndroid Build Coastguard Worker [Arguments(3)] 125*1b3f573fSAndroid Build Coastguard Worker [Arguments(4)] 126*1b3f573fSAndroid Build Coastguard Worker [Arguments(5)] ParseRawVarint32_ParseContext(int encodedSize)127*1b3f573fSAndroid Build Coastguard Worker public int ParseRawVarint32_ParseContext(int encodedSize) 128*1b3f573fSAndroid Build Coastguard Worker { 129*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(varintInputBuffers[encodedSize], out ParseContext ctx); 130*1b3f573fSAndroid Build Coastguard Worker int sum = 0; 131*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 132*1b3f573fSAndroid Build Coastguard Worker { 133*1b3f573fSAndroid Build Coastguard Worker sum += ctx.ReadInt32(); 134*1b3f573fSAndroid Build Coastguard Worker } 135*1b3f573fSAndroid Build Coastguard Worker return sum; 136*1b3f573fSAndroid Build Coastguard Worker } 137*1b3f573fSAndroid Build Coastguard Worker 138*1b3f573fSAndroid Build Coastguard Worker [Benchmark] 139*1b3f573fSAndroid Build Coastguard Worker [Arguments(1)] 140*1b3f573fSAndroid Build Coastguard Worker [Arguments(2)] 141*1b3f573fSAndroid Build Coastguard Worker [Arguments(3)] 142*1b3f573fSAndroid Build Coastguard Worker [Arguments(4)] 143*1b3f573fSAndroid Build Coastguard Worker [Arguments(5)] 144*1b3f573fSAndroid Build Coastguard Worker [Arguments(6)] 145*1b3f573fSAndroid Build Coastguard Worker [Arguments(7)] 146*1b3f573fSAndroid Build Coastguard Worker [Arguments(8)] 147*1b3f573fSAndroid Build Coastguard Worker [Arguments(9)] 148*1b3f573fSAndroid Build Coastguard Worker [Arguments(10)] ParseRawVarint64_CodedInputStream(int encodedSize)149*1b3f573fSAndroid Build Coastguard Worker public long ParseRawVarint64_CodedInputStream(int encodedSize) 150*1b3f573fSAndroid Build Coastguard Worker { 151*1b3f573fSAndroid Build Coastguard Worker CodedInputStream cis = new CodedInputStream(varintInputBuffers[encodedSize]); 152*1b3f573fSAndroid Build Coastguard Worker long sum = 0; 153*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 154*1b3f573fSAndroid Build Coastguard Worker { 155*1b3f573fSAndroid Build Coastguard Worker sum += cis.ReadInt64(); 156*1b3f573fSAndroid Build Coastguard Worker } 157*1b3f573fSAndroid Build Coastguard Worker return sum; 158*1b3f573fSAndroid Build Coastguard Worker } 159*1b3f573fSAndroid Build Coastguard Worker 160*1b3f573fSAndroid Build Coastguard Worker [Benchmark] 161*1b3f573fSAndroid Build Coastguard Worker [Arguments(1)] 162*1b3f573fSAndroid Build Coastguard Worker [Arguments(2)] 163*1b3f573fSAndroid Build Coastguard Worker [Arguments(3)] 164*1b3f573fSAndroid Build Coastguard Worker [Arguments(4)] 165*1b3f573fSAndroid Build Coastguard Worker [Arguments(5)] 166*1b3f573fSAndroid Build Coastguard Worker [Arguments(6)] 167*1b3f573fSAndroid Build Coastguard Worker [Arguments(7)] 168*1b3f573fSAndroid Build Coastguard Worker [Arguments(8)] 169*1b3f573fSAndroid Build Coastguard Worker [Arguments(9)] 170*1b3f573fSAndroid Build Coastguard Worker [Arguments(10)] ParseRawVarint64_ParseContext(int encodedSize)171*1b3f573fSAndroid Build Coastguard Worker public long ParseRawVarint64_ParseContext(int encodedSize) 172*1b3f573fSAndroid Build Coastguard Worker { 173*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(varintInputBuffers[encodedSize], out ParseContext ctx); 174*1b3f573fSAndroid Build Coastguard Worker long sum = 0; 175*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 176*1b3f573fSAndroid Build Coastguard Worker { 177*1b3f573fSAndroid Build Coastguard Worker sum += ctx.ReadInt64(); 178*1b3f573fSAndroid Build Coastguard Worker } 179*1b3f573fSAndroid Build Coastguard Worker return sum; 180*1b3f573fSAndroid Build Coastguard Worker } 181*1b3f573fSAndroid Build Coastguard Worker 182*1b3f573fSAndroid Build Coastguard Worker [Benchmark] ParseFixed32_CodedInputStream()183*1b3f573fSAndroid Build Coastguard Worker public uint ParseFixed32_CodedInputStream() 184*1b3f573fSAndroid Build Coastguard Worker { 185*1b3f573fSAndroid Build Coastguard Worker const int encodedSize = sizeof(uint); 186*1b3f573fSAndroid Build Coastguard Worker CodedInputStream cis = new CodedInputStream(fixedIntInputBuffer); 187*1b3f573fSAndroid Build Coastguard Worker uint sum = 0; 188*1b3f573fSAndroid Build Coastguard Worker for (uint i = 0; i < BytesToParse / encodedSize; i++) 189*1b3f573fSAndroid Build Coastguard Worker { 190*1b3f573fSAndroid Build Coastguard Worker sum += cis.ReadFixed32(); 191*1b3f573fSAndroid Build Coastguard Worker } 192*1b3f573fSAndroid Build Coastguard Worker return sum; 193*1b3f573fSAndroid Build Coastguard Worker } 194*1b3f573fSAndroid Build Coastguard Worker 195*1b3f573fSAndroid Build Coastguard Worker [Benchmark] ParseFixed32_ParseContext()196*1b3f573fSAndroid Build Coastguard Worker public uint ParseFixed32_ParseContext() 197*1b3f573fSAndroid Build Coastguard Worker { 198*1b3f573fSAndroid Build Coastguard Worker const int encodedSize = sizeof(uint); 199*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(fixedIntInputBuffer, out ParseContext ctx); 200*1b3f573fSAndroid Build Coastguard Worker uint sum = 0; 201*1b3f573fSAndroid Build Coastguard Worker for (uint i = 0; i < BytesToParse / encodedSize; i++) 202*1b3f573fSAndroid Build Coastguard Worker { 203*1b3f573fSAndroid Build Coastguard Worker sum += ctx.ReadFixed32(); 204*1b3f573fSAndroid Build Coastguard Worker } 205*1b3f573fSAndroid Build Coastguard Worker return sum; 206*1b3f573fSAndroid Build Coastguard Worker } 207*1b3f573fSAndroid Build Coastguard Worker 208*1b3f573fSAndroid Build Coastguard Worker [Benchmark] ParseFixed64_CodedInputStream()209*1b3f573fSAndroid Build Coastguard Worker public ulong ParseFixed64_CodedInputStream() 210*1b3f573fSAndroid Build Coastguard Worker { 211*1b3f573fSAndroid Build Coastguard Worker const int encodedSize = sizeof(ulong); 212*1b3f573fSAndroid Build Coastguard Worker CodedInputStream cis = new CodedInputStream(fixedIntInputBuffer); 213*1b3f573fSAndroid Build Coastguard Worker ulong sum = 0; 214*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 215*1b3f573fSAndroid Build Coastguard Worker { 216*1b3f573fSAndroid Build Coastguard Worker sum += cis.ReadFixed64(); 217*1b3f573fSAndroid Build Coastguard Worker } 218*1b3f573fSAndroid Build Coastguard Worker return sum; 219*1b3f573fSAndroid Build Coastguard Worker } 220*1b3f573fSAndroid Build Coastguard Worker 221*1b3f573fSAndroid Build Coastguard Worker [Benchmark] ParseFixed64_ParseContext()222*1b3f573fSAndroid Build Coastguard Worker public ulong ParseFixed64_ParseContext() 223*1b3f573fSAndroid Build Coastguard Worker { 224*1b3f573fSAndroid Build Coastguard Worker const int encodedSize = sizeof(ulong); 225*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(fixedIntInputBuffer, out ParseContext ctx); 226*1b3f573fSAndroid Build Coastguard Worker ulong sum = 0; 227*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 228*1b3f573fSAndroid Build Coastguard Worker { 229*1b3f573fSAndroid Build Coastguard Worker sum += ctx.ReadFixed64(); 230*1b3f573fSAndroid Build Coastguard Worker } 231*1b3f573fSAndroid Build Coastguard Worker return sum; 232*1b3f573fSAndroid Build Coastguard Worker } 233*1b3f573fSAndroid Build Coastguard Worker 234*1b3f573fSAndroid Build Coastguard Worker [Benchmark] ParseRawFloat_CodedInputStream()235*1b3f573fSAndroid Build Coastguard Worker public float ParseRawFloat_CodedInputStream() 236*1b3f573fSAndroid Build Coastguard Worker { 237*1b3f573fSAndroid Build Coastguard Worker const int encodedSize = sizeof(float); 238*1b3f573fSAndroid Build Coastguard Worker CodedInputStream cis = new CodedInputStream(floatInputBuffer); 239*1b3f573fSAndroid Build Coastguard Worker float sum = 0; 240*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 241*1b3f573fSAndroid Build Coastguard Worker { 242*1b3f573fSAndroid Build Coastguard Worker sum += cis.ReadFloat(); 243*1b3f573fSAndroid Build Coastguard Worker } 244*1b3f573fSAndroid Build Coastguard Worker return sum; 245*1b3f573fSAndroid Build Coastguard Worker } 246*1b3f573fSAndroid Build Coastguard Worker 247*1b3f573fSAndroid Build Coastguard Worker [Benchmark] ParseRawFloat_ParseContext()248*1b3f573fSAndroid Build Coastguard Worker public float ParseRawFloat_ParseContext() 249*1b3f573fSAndroid Build Coastguard Worker { 250*1b3f573fSAndroid Build Coastguard Worker const int encodedSize = sizeof(float); 251*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(floatInputBuffer, out ParseContext ctx); 252*1b3f573fSAndroid Build Coastguard Worker float sum = 0; 253*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 254*1b3f573fSAndroid Build Coastguard Worker { 255*1b3f573fSAndroid Build Coastguard Worker sum += ctx.ReadFloat(); 256*1b3f573fSAndroid Build Coastguard Worker } 257*1b3f573fSAndroid Build Coastguard Worker return sum; 258*1b3f573fSAndroid Build Coastguard Worker } 259*1b3f573fSAndroid Build Coastguard Worker 260*1b3f573fSAndroid Build Coastguard Worker [Benchmark] ParseRawDouble_CodedInputStream()261*1b3f573fSAndroid Build Coastguard Worker public double ParseRawDouble_CodedInputStream() 262*1b3f573fSAndroid Build Coastguard Worker { 263*1b3f573fSAndroid Build Coastguard Worker const int encodedSize = sizeof(double); 264*1b3f573fSAndroid Build Coastguard Worker CodedInputStream cis = new CodedInputStream(doubleInputBuffer); 265*1b3f573fSAndroid Build Coastguard Worker double sum = 0; 266*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 267*1b3f573fSAndroid Build Coastguard Worker { 268*1b3f573fSAndroid Build Coastguard Worker sum += cis.ReadDouble(); 269*1b3f573fSAndroid Build Coastguard Worker } 270*1b3f573fSAndroid Build Coastguard Worker return sum; 271*1b3f573fSAndroid Build Coastguard Worker } 272*1b3f573fSAndroid Build Coastguard Worker 273*1b3f573fSAndroid Build Coastguard Worker [Benchmark] ParseRawDouble_ParseContext()274*1b3f573fSAndroid Build Coastguard Worker public double ParseRawDouble_ParseContext() 275*1b3f573fSAndroid Build Coastguard Worker { 276*1b3f573fSAndroid Build Coastguard Worker const int encodedSize = sizeof(double); 277*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(doubleInputBuffer, out ParseContext ctx); 278*1b3f573fSAndroid Build Coastguard Worker double sum = 0; 279*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 280*1b3f573fSAndroid Build Coastguard Worker { 281*1b3f573fSAndroid Build Coastguard Worker sum += ctx.ReadDouble(); 282*1b3f573fSAndroid Build Coastguard Worker } 283*1b3f573fSAndroid Build Coastguard Worker return sum; 284*1b3f573fSAndroid Build Coastguard Worker } 285*1b3f573fSAndroid Build Coastguard Worker 286*1b3f573fSAndroid Build Coastguard Worker [Benchmark] 287*1b3f573fSAndroid Build Coastguard Worker [ArgumentsSource(nameof(StringEncodedSizes))] ParseString_CodedInputStream(int encodedSize)288*1b3f573fSAndroid Build Coastguard Worker public int ParseString_CodedInputStream(int encodedSize) 289*1b3f573fSAndroid Build Coastguard Worker { 290*1b3f573fSAndroid Build Coastguard Worker CodedInputStream cis = new CodedInputStream(stringInputBuffers[encodedSize]); 291*1b3f573fSAndroid Build Coastguard Worker int sum = 0; 292*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 293*1b3f573fSAndroid Build Coastguard Worker { 294*1b3f573fSAndroid Build Coastguard Worker sum += cis.ReadString().Length; 295*1b3f573fSAndroid Build Coastguard Worker } 296*1b3f573fSAndroid Build Coastguard Worker return sum; 297*1b3f573fSAndroid Build Coastguard Worker } 298*1b3f573fSAndroid Build Coastguard Worker 299*1b3f573fSAndroid Build Coastguard Worker [Benchmark] 300*1b3f573fSAndroid Build Coastguard Worker [ArgumentsSource(nameof(StringEncodedSizes))] ParseString_ParseContext(int encodedSize)301*1b3f573fSAndroid Build Coastguard Worker public int ParseString_ParseContext(int encodedSize) 302*1b3f573fSAndroid Build Coastguard Worker { 303*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(stringInputBuffers[encodedSize], out ParseContext ctx); 304*1b3f573fSAndroid Build Coastguard Worker int sum = 0; 305*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 306*1b3f573fSAndroid Build Coastguard Worker { 307*1b3f573fSAndroid Build Coastguard Worker sum += ctx.ReadString().Length; 308*1b3f573fSAndroid Build Coastguard Worker } 309*1b3f573fSAndroid Build Coastguard Worker return sum; 310*1b3f573fSAndroid Build Coastguard Worker } 311*1b3f573fSAndroid Build Coastguard Worker 312*1b3f573fSAndroid Build Coastguard Worker [Benchmark] 313*1b3f573fSAndroid Build Coastguard Worker [ArgumentsSource(nameof(StringSegmentedEncodedSizes))] ParseString_ParseContext_MultipleSegments(int encodedSize)314*1b3f573fSAndroid Build Coastguard Worker public int ParseString_ParseContext_MultipleSegments(int encodedSize) 315*1b3f573fSAndroid Build Coastguard Worker { 316*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(stringInputBuffersSegmented[encodedSize], out ParseContext ctx); 317*1b3f573fSAndroid Build Coastguard Worker int sum = 0; 318*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 319*1b3f573fSAndroid Build Coastguard Worker { 320*1b3f573fSAndroid Build Coastguard Worker sum += ctx.ReadString().Length; 321*1b3f573fSAndroid Build Coastguard Worker } 322*1b3f573fSAndroid Build Coastguard Worker return sum; 323*1b3f573fSAndroid Build Coastguard Worker } 324*1b3f573fSAndroid Build Coastguard Worker 325*1b3f573fSAndroid Build Coastguard Worker [Benchmark] 326*1b3f573fSAndroid Build Coastguard Worker [ArgumentsSource(nameof(StringEncodedSizes))] ParseBytes_CodedInputStream(int encodedSize)327*1b3f573fSAndroid Build Coastguard Worker public int ParseBytes_CodedInputStream(int encodedSize) 328*1b3f573fSAndroid Build Coastguard Worker { 329*1b3f573fSAndroid Build Coastguard Worker CodedInputStream cis = new CodedInputStream(stringInputBuffers[encodedSize]); 330*1b3f573fSAndroid Build Coastguard Worker int sum = 0; 331*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 332*1b3f573fSAndroid Build Coastguard Worker { 333*1b3f573fSAndroid Build Coastguard Worker sum += cis.ReadBytes().Length; 334*1b3f573fSAndroid Build Coastguard Worker } 335*1b3f573fSAndroid Build Coastguard Worker return sum; 336*1b3f573fSAndroid Build Coastguard Worker } 337*1b3f573fSAndroid Build Coastguard Worker 338*1b3f573fSAndroid Build Coastguard Worker [Benchmark] 339*1b3f573fSAndroid Build Coastguard Worker [ArgumentsSource(nameof(StringEncodedSizes))] ParseBytes_ParseContext(int encodedSize)340*1b3f573fSAndroid Build Coastguard Worker public int ParseBytes_ParseContext(int encodedSize) 341*1b3f573fSAndroid Build Coastguard Worker { 342*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(stringInputBuffers[encodedSize], out ParseContext ctx); 343*1b3f573fSAndroid Build Coastguard Worker int sum = 0; 344*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 345*1b3f573fSAndroid Build Coastguard Worker { 346*1b3f573fSAndroid Build Coastguard Worker sum += ctx.ReadBytes().Length; 347*1b3f573fSAndroid Build Coastguard Worker } 348*1b3f573fSAndroid Build Coastguard Worker return sum; 349*1b3f573fSAndroid Build Coastguard Worker } 350*1b3f573fSAndroid Build Coastguard Worker 351*1b3f573fSAndroid Build Coastguard Worker [Benchmark] 352*1b3f573fSAndroid Build Coastguard Worker [ArgumentsSource(nameof(StringSegmentedEncodedSizes))] ParseBytes_ParseContext_MultipleSegments(int encodedSize)353*1b3f573fSAndroid Build Coastguard Worker public int ParseBytes_ParseContext_MultipleSegments(int encodedSize) 354*1b3f573fSAndroid Build Coastguard Worker { 355*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(stringInputBuffersSegmented[encodedSize], out ParseContext ctx); 356*1b3f573fSAndroid Build Coastguard Worker int sum = 0; 357*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < BytesToParse / encodedSize; i++) 358*1b3f573fSAndroid Build Coastguard Worker { 359*1b3f573fSAndroid Build Coastguard Worker sum += ctx.ReadBytes().Length; 360*1b3f573fSAndroid Build Coastguard Worker } 361*1b3f573fSAndroid Build Coastguard Worker return sum; 362*1b3f573fSAndroid Build Coastguard Worker } 363*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(byte[] buffer, out ParseContext ctx)364*1b3f573fSAndroid Build Coastguard Worker private static void InitializeParseContext(byte[] buffer, out ParseContext ctx) 365*1b3f573fSAndroid Build Coastguard Worker { 366*1b3f573fSAndroid Build Coastguard Worker ParseContext.Initialize(new ReadOnlySequence<byte>(buffer), out ctx); 367*1b3f573fSAndroid Build Coastguard Worker } 368*1b3f573fSAndroid Build Coastguard Worker InitializeParseContext(ReadOnlySequence<byte> buffer, out ParseContext ctx)369*1b3f573fSAndroid Build Coastguard Worker private static void InitializeParseContext(ReadOnlySequence<byte> buffer, out ParseContext ctx) 370*1b3f573fSAndroid Build Coastguard Worker { 371*1b3f573fSAndroid Build Coastguard Worker ParseContext.Initialize(buffer, out ctx); 372*1b3f573fSAndroid Build Coastguard Worker } 373*1b3f573fSAndroid Build Coastguard Worker CreateBufferWithRandomVarints(Random random, int valueCount, int encodedSize, int paddingValueCount)374*1b3f573fSAndroid Build Coastguard Worker private static byte[] CreateBufferWithRandomVarints(Random random, int valueCount, int encodedSize, int paddingValueCount) 375*1b3f573fSAndroid Build Coastguard Worker { 376*1b3f573fSAndroid Build Coastguard Worker MemoryStream ms = new MemoryStream(); 377*1b3f573fSAndroid Build Coastguard Worker CodedOutputStream cos = new CodedOutputStream(ms); 378*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < valueCount + paddingValueCount; i++) 379*1b3f573fSAndroid Build Coastguard Worker { 380*1b3f573fSAndroid Build Coastguard Worker cos.WriteUInt64(RandomUnsignedVarint(random, encodedSize, false)); 381*1b3f573fSAndroid Build Coastguard Worker } 382*1b3f573fSAndroid Build Coastguard Worker cos.Flush(); 383*1b3f573fSAndroid Build Coastguard Worker var buffer = ms.ToArray(); 384*1b3f573fSAndroid Build Coastguard Worker 385*1b3f573fSAndroid Build Coastguard Worker if (buffer.Length != encodedSize * (valueCount + paddingValueCount)) 386*1b3f573fSAndroid Build Coastguard Worker { 387*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException($"Unexpected output buffer length {buffer.Length}"); 388*1b3f573fSAndroid Build Coastguard Worker } 389*1b3f573fSAndroid Build Coastguard Worker return buffer; 390*1b3f573fSAndroid Build Coastguard Worker } 391*1b3f573fSAndroid Build Coastguard Worker CreateBufferWithRandomFloats(Random random, int valueCount, int paddingValueCount)392*1b3f573fSAndroid Build Coastguard Worker private static byte[] CreateBufferWithRandomFloats(Random random, int valueCount, int paddingValueCount) 393*1b3f573fSAndroid Build Coastguard Worker { 394*1b3f573fSAndroid Build Coastguard Worker MemoryStream ms = new MemoryStream(); 395*1b3f573fSAndroid Build Coastguard Worker CodedOutputStream cos = new CodedOutputStream(ms); 396*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < valueCount + paddingValueCount; i++) 397*1b3f573fSAndroid Build Coastguard Worker { 398*1b3f573fSAndroid Build Coastguard Worker cos.WriteFloat((float)random.NextDouble()); 399*1b3f573fSAndroid Build Coastguard Worker } 400*1b3f573fSAndroid Build Coastguard Worker cos.Flush(); 401*1b3f573fSAndroid Build Coastguard Worker var buffer = ms.ToArray(); 402*1b3f573fSAndroid Build Coastguard Worker return buffer; 403*1b3f573fSAndroid Build Coastguard Worker } 404*1b3f573fSAndroid Build Coastguard Worker CreateBufferWithRandomDoubles(Random random, int valueCount, int paddingValueCount)405*1b3f573fSAndroid Build Coastguard Worker private static byte[] CreateBufferWithRandomDoubles(Random random, int valueCount, int paddingValueCount) 406*1b3f573fSAndroid Build Coastguard Worker { 407*1b3f573fSAndroid Build Coastguard Worker MemoryStream ms = new MemoryStream(); 408*1b3f573fSAndroid Build Coastguard Worker CodedOutputStream cos = new CodedOutputStream(ms); 409*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < valueCount + paddingValueCount; i++) 410*1b3f573fSAndroid Build Coastguard Worker { 411*1b3f573fSAndroid Build Coastguard Worker cos.WriteDouble(random.NextDouble()); 412*1b3f573fSAndroid Build Coastguard Worker } 413*1b3f573fSAndroid Build Coastguard Worker cos.Flush(); 414*1b3f573fSAndroid Build Coastguard Worker var buffer = ms.ToArray(); 415*1b3f573fSAndroid Build Coastguard Worker return buffer; 416*1b3f573fSAndroid Build Coastguard Worker } 417*1b3f573fSAndroid Build Coastguard Worker CreateBufferWithRandomData(Random random, int valueCount, int encodedSize, int paddingValueCount)418*1b3f573fSAndroid Build Coastguard Worker private static byte[] CreateBufferWithRandomData(Random random, int valueCount, int encodedSize, int paddingValueCount) 419*1b3f573fSAndroid Build Coastguard Worker { 420*1b3f573fSAndroid Build Coastguard Worker int bufferSize = (valueCount + paddingValueCount) * encodedSize; 421*1b3f573fSAndroid Build Coastguard Worker byte[] buffer = new byte[bufferSize]; 422*1b3f573fSAndroid Build Coastguard Worker random.NextBytes(buffer); 423*1b3f573fSAndroid Build Coastguard Worker return buffer; 424*1b3f573fSAndroid Build Coastguard Worker } 425*1b3f573fSAndroid Build Coastguard Worker 426*1b3f573fSAndroid Build Coastguard Worker /// <summary> 427*1b3f573fSAndroid Build Coastguard Worker /// Generate a random value that will take exactly "encodedSize" bytes when varint-encoded. 428*1b3f573fSAndroid Build Coastguard Worker /// </summary> RandomUnsignedVarint(Random random, int encodedSize, bool fitsIn32Bits)429*1b3f573fSAndroid Build Coastguard Worker public static ulong RandomUnsignedVarint(Random random, int encodedSize, bool fitsIn32Bits) 430*1b3f573fSAndroid Build Coastguard Worker { 431*1b3f573fSAndroid Build Coastguard Worker Span<byte> randomBytesBuffer = stackalloc byte[8]; 432*1b3f573fSAndroid Build Coastguard Worker 433*1b3f573fSAndroid Build Coastguard Worker if (encodedSize < 1 || encodedSize > 10 || (fitsIn32Bits && encodedSize > 5)) 434*1b3f573fSAndroid Build Coastguard Worker { 435*1b3f573fSAndroid Build Coastguard Worker throw new ArgumentException("Illegal encodedSize value requested", nameof(encodedSize)); 436*1b3f573fSAndroid Build Coastguard Worker } 437*1b3f573fSAndroid Build Coastguard Worker const int bitsPerByte = 7; 438*1b3f573fSAndroid Build Coastguard Worker 439*1b3f573fSAndroid Build Coastguard Worker ulong result = 0; 440*1b3f573fSAndroid Build Coastguard Worker while (true) 441*1b3f573fSAndroid Build Coastguard Worker { 442*1b3f573fSAndroid Build Coastguard Worker random.NextBytes(randomBytesBuffer); 443*1b3f573fSAndroid Build Coastguard Worker ulong randomValue = BinaryPrimitives.ReadUInt64LittleEndian(randomBytesBuffer); 444*1b3f573fSAndroid Build Coastguard Worker 445*1b3f573fSAndroid Build Coastguard Worker // only use the number of random bits we need 446*1b3f573fSAndroid Build Coastguard Worker ulong bitmask = encodedSize < 10 ? ((1UL << (encodedSize * bitsPerByte)) - 1) : ulong.MaxValue; 447*1b3f573fSAndroid Build Coastguard Worker result = randomValue & bitmask; 448*1b3f573fSAndroid Build Coastguard Worker 449*1b3f573fSAndroid Build Coastguard Worker if (fitsIn32Bits) 450*1b3f573fSAndroid Build Coastguard Worker { 451*1b3f573fSAndroid Build Coastguard Worker // make sure the resulting value is representable by a uint. 452*1b3f573fSAndroid Build Coastguard Worker result &= uint.MaxValue; 453*1b3f573fSAndroid Build Coastguard Worker } 454*1b3f573fSAndroid Build Coastguard Worker 455*1b3f573fSAndroid Build Coastguard Worker if (encodedSize == 10) 456*1b3f573fSAndroid Build Coastguard Worker { 457*1b3f573fSAndroid Build Coastguard Worker // for 10-byte values the highest bit always needs to be set (7*9=63) 458*1b3f573fSAndroid Build Coastguard Worker result |= ulong.MaxValue; 459*1b3f573fSAndroid Build Coastguard Worker break; 460*1b3f573fSAndroid Build Coastguard Worker } 461*1b3f573fSAndroid Build Coastguard Worker 462*1b3f573fSAndroid Build Coastguard Worker // some random values won't require the full "encodedSize" bytes, check that at least 463*1b3f573fSAndroid Build Coastguard Worker // one of the top 7 bits is set. Retrying is fine since it only happens rarely 464*1b3f573fSAndroid Build Coastguard Worker if (encodedSize == 1 || (result & (0x7FUL << ((encodedSize - 1) * bitsPerByte))) != 0) 465*1b3f573fSAndroid Build Coastguard Worker { 466*1b3f573fSAndroid Build Coastguard Worker break; 467*1b3f573fSAndroid Build Coastguard Worker } 468*1b3f573fSAndroid Build Coastguard Worker } 469*1b3f573fSAndroid Build Coastguard Worker return result; 470*1b3f573fSAndroid Build Coastguard Worker } 471*1b3f573fSAndroid Build Coastguard Worker CreateBufferWithStrings(int valueCount, int encodedSize, int paddingValueCount)472*1b3f573fSAndroid Build Coastguard Worker private static byte[] CreateBufferWithStrings(int valueCount, int encodedSize, int paddingValueCount) 473*1b3f573fSAndroid Build Coastguard Worker { 474*1b3f573fSAndroid Build Coastguard Worker var str = CreateStringWithEncodedSize(encodedSize); 475*1b3f573fSAndroid Build Coastguard Worker 476*1b3f573fSAndroid Build Coastguard Worker MemoryStream ms = new MemoryStream(); 477*1b3f573fSAndroid Build Coastguard Worker CodedOutputStream cos = new CodedOutputStream(ms); 478*1b3f573fSAndroid Build Coastguard Worker for (int i = 0; i < valueCount + paddingValueCount; i++) 479*1b3f573fSAndroid Build Coastguard Worker { 480*1b3f573fSAndroid Build Coastguard Worker cos.WriteString(str); 481*1b3f573fSAndroid Build Coastguard Worker } 482*1b3f573fSAndroid Build Coastguard Worker cos.Flush(); 483*1b3f573fSAndroid Build Coastguard Worker var buffer = ms.ToArray(); 484*1b3f573fSAndroid Build Coastguard Worker 485*1b3f573fSAndroid Build Coastguard Worker if (buffer.Length != encodedSize * (valueCount + paddingValueCount)) 486*1b3f573fSAndroid Build Coastguard Worker { 487*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException($"Unexpected output buffer length {buffer.Length}"); 488*1b3f573fSAndroid Build Coastguard Worker } 489*1b3f573fSAndroid Build Coastguard Worker return buffer; 490*1b3f573fSAndroid Build Coastguard Worker } 491*1b3f573fSAndroid Build Coastguard Worker CreateStringWithEncodedSize(int encodedSize)492*1b3f573fSAndroid Build Coastguard Worker public static string CreateStringWithEncodedSize(int encodedSize) 493*1b3f573fSAndroid Build Coastguard Worker { 494*1b3f573fSAndroid Build Coastguard Worker var str = new string('a', encodedSize); 495*1b3f573fSAndroid Build Coastguard Worker while (CodedOutputStream.ComputeStringSize(str) > encodedSize) 496*1b3f573fSAndroid Build Coastguard Worker { 497*1b3f573fSAndroid Build Coastguard Worker str = str.Substring(1); 498*1b3f573fSAndroid Build Coastguard Worker } 499*1b3f573fSAndroid Build Coastguard Worker 500*1b3f573fSAndroid Build Coastguard Worker if (CodedOutputStream.ComputeStringSize(str) != encodedSize) 501*1b3f573fSAndroid Build Coastguard Worker { 502*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException($"Generated string with wrong encodedSize"); 503*1b3f573fSAndroid Build Coastguard Worker } 504*1b3f573fSAndroid Build Coastguard Worker return str; 505*1b3f573fSAndroid Build Coastguard Worker } 506*1b3f573fSAndroid Build Coastguard Worker CreateNonAsciiStringWithEncodedSize(int encodedSize)507*1b3f573fSAndroid Build Coastguard Worker public static string CreateNonAsciiStringWithEncodedSize(int encodedSize) 508*1b3f573fSAndroid Build Coastguard Worker { 509*1b3f573fSAndroid Build Coastguard Worker if (encodedSize < 3) 510*1b3f573fSAndroid Build Coastguard Worker { 511*1b3f573fSAndroid Build Coastguard Worker throw new ArgumentException("Illegal encoded size for a string with non-ascii chars."); 512*1b3f573fSAndroid Build Coastguard Worker } 513*1b3f573fSAndroid Build Coastguard Worker var twoByteChar = '\u00DC'; // U-umlaut, UTF8 encoding has 2 bytes 514*1b3f573fSAndroid Build Coastguard Worker var str = new string(twoByteChar, encodedSize / 2); 515*1b3f573fSAndroid Build Coastguard Worker while (CodedOutputStream.ComputeStringSize(str) > encodedSize) 516*1b3f573fSAndroid Build Coastguard Worker { 517*1b3f573fSAndroid Build Coastguard Worker str = str.Substring(1); 518*1b3f573fSAndroid Build Coastguard Worker } 519*1b3f573fSAndroid Build Coastguard Worker 520*1b3f573fSAndroid Build Coastguard Worker // add padding of ascii characters to reach the desired encoded size. 521*1b3f573fSAndroid Build Coastguard Worker while (CodedOutputStream.ComputeStringSize(str) < encodedSize) 522*1b3f573fSAndroid Build Coastguard Worker { 523*1b3f573fSAndroid Build Coastguard Worker str += 'a'; 524*1b3f573fSAndroid Build Coastguard Worker } 525*1b3f573fSAndroid Build Coastguard Worker 526*1b3f573fSAndroid Build Coastguard Worker // Note that for a few specific encodedSize values, it might be impossible to generate 527*1b3f573fSAndroid Build Coastguard Worker // the string with the desired encodedSize using the algorithm above. For testing purposes, checking that 528*1b3f573fSAndroid Build Coastguard Worker // the encoded size we got is actually correct is good enough. 529*1b3f573fSAndroid Build Coastguard Worker if (CodedOutputStream.ComputeStringSize(str) != encodedSize) 530*1b3f573fSAndroid Build Coastguard Worker { 531*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException($"Generated string with wrong encodedSize"); 532*1b3f573fSAndroid Build Coastguard Worker } 533*1b3f573fSAndroid Build Coastguard Worker return str; 534*1b3f573fSAndroid Build Coastguard Worker } 535*1b3f573fSAndroid Build Coastguard Worker } 536*1b3f573fSAndroid Build Coastguard Worker } 537