| #region Copyright notice and license | 
 | // Protocol Buffers - Google's data interchange format | 
 | // Copyright 2019 Google Inc.  All rights reserved. | 
 | // https://github.com/protocolbuffers/protobuf | 
 | // | 
 | // Redistribution and use in source and binary forms, with or without | 
 | // modification, are permitted provided that the following conditions are | 
 | // met: | 
 | // | 
 | //     * Redistributions of source code must retain the above copyright | 
 | // notice, this list of conditions and the following disclaimer. | 
 | //     * Redistributions in binary form must reproduce the above | 
 | // copyright notice, this list of conditions and the following disclaimer | 
 | // in the documentation and/or other materials provided with the | 
 | // distribution. | 
 | //     * Neither the name of Google Inc. nor the names of its | 
 | // contributors may be used to endorse or promote products derived from | 
 | // this software without specific prior written permission. | 
 | // | 
 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 | #endregion | 
 |  | 
 | using BenchmarkDotNet.Attributes; | 
 | using System; | 
 | using System.Buffers.Binary; | 
 | using System.Collections.Generic; | 
 | using System.IO; | 
 | using System.Buffers; | 
 | using System.Text; | 
 |  | 
 | namespace Google.Protobuf.Benchmarks | 
 | { | 
 |     /// <summary> | 
 |     /// Benchmarks throughput when writing raw primitives. | 
 |     /// </summary> | 
 |     [MemoryDiagnoser] | 
 |     public class WriteRawPrimitivesBenchmark | 
 |     { | 
 |         // key is the encodedSize of varint values | 
 |         Dictionary<int, uint[]> varint32Values; | 
 |         Dictionary<int, ulong[]> varint64Values; | 
 |  | 
 |         double[] doubleValues; | 
 |         float[] floatValues; | 
 |  | 
 |         // key is the encodedSize of string values | 
 |         Dictionary<int, string[]> stringValues; | 
 |  | 
 |         // key is the encodedSize of string values | 
 |         Dictionary<int, string[]> nonAsciiStringValues; | 
 |  | 
 |         // key is the encodedSize of string values | 
 |         Dictionary<int, ByteString[]> byteStringValues; | 
 |  | 
 |         // the buffer to which all the data will be written | 
 |         byte[] outputBuffer; | 
 |  | 
 |         Random random = new Random(417384220);  // random but deterministic seed | 
 |  | 
 |         public IEnumerable<int> StringEncodedSizes => new[] { 1, 4, 10, 105, 10080 }; | 
 |  | 
 |         public IEnumerable<int> NonAsciiStringEncodedSizes => new[] { 4, 10, 105, 10080 }; | 
 |  | 
 |         [GlobalSetup] | 
 |         public void GlobalSetup() | 
 |         { | 
 |             outputBuffer = new byte[BytesToWrite]; | 
 |  | 
 |             varint32Values = new Dictionary<int, uint[]>(); | 
 |             varint64Values = new Dictionary<int, ulong[]>(); | 
 |             for (int encodedSize = 1; encodedSize <= 10; encodedSize++) | 
 |             { | 
 |                 if (encodedSize <= 5) | 
 |                 { | 
 |                     varint32Values.Add(encodedSize, CreateRandomVarints32(random, BytesToWrite / encodedSize, encodedSize)); | 
 |                 } | 
 |                 varint64Values.Add(encodedSize, CreateRandomVarints64(random, BytesToWrite / encodedSize, encodedSize)); | 
 |             } | 
 |  | 
 |             doubleValues = CreateRandomDoubles(random, BytesToWrite / sizeof(double)); | 
 |             floatValues = CreateRandomFloats(random, BytesToWrite / sizeof(float)); | 
 |  | 
 |             stringValues = new Dictionary<int, string[]>(); | 
 |  | 
 |             byteStringValues = new Dictionary<int, ByteString[]>(); | 
 |             foreach(var encodedSize in StringEncodedSizes) | 
 |             { | 
 |                 stringValues.Add(encodedSize, CreateStrings(BytesToWrite / encodedSize, encodedSize)); | 
 |                 byteStringValues.Add(encodedSize, CreateByteStrings(BytesToWrite / encodedSize, encodedSize)); | 
 |             } | 
 |  | 
 |             nonAsciiStringValues = new Dictionary<int, string[]>(); | 
 |             foreach(var encodedSize in NonAsciiStringEncodedSizes) | 
 |             { | 
 |                 nonAsciiStringValues.Add(encodedSize, CreateNonAsciiStrings(BytesToWrite / encodedSize, encodedSize)); | 
 |             } | 
 |         } | 
 |  | 
 |         // Total number of bytes that each benchmark will write. | 
 |         // Measuring the time taken to write buffer of given size makes it easier to compare parsing speed for different | 
 |         // types and makes it easy to calculate the througput (in MB/s) | 
 |         // 10800 bytes is chosen because it is divisible by all possible encoded sizes for all primitive types {1..10} | 
 |         [Params(10080)] | 
 |         public int BytesToWrite { get; set; } | 
 |  | 
 |         [Benchmark] | 
 |         [Arguments(1)] | 
 |         [Arguments(2)] | 
 |         [Arguments(3)] | 
 |         [Arguments(4)] | 
 |         [Arguments(5)] | 
 |         public void WriteRawVarint32_CodedOutputStream(int encodedSize) | 
 |         { | 
 |             var values = varint32Values[encodedSize]; | 
 |             var cos = new CodedOutputStream(outputBuffer); | 
 |             for (int i = 0; i < values.Length; i++) | 
 |             { | 
 |                 cos.WriteRawVarint32(values[i]); | 
 |             } | 
 |             cos.Flush(); | 
 |             cos.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         [Arguments(1)] | 
 |         [Arguments(2)] | 
 |         [Arguments(3)] | 
 |         [Arguments(4)] | 
 |         [Arguments(5)] | 
 |         public void WriteRawVarint32_WriteContext(int encodedSize) | 
 |         { | 
 |             var values = varint32Values[encodedSize]; | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             for (int i = 0; i < values.Length; i++) | 
 |             { | 
 |                 ctx.WriteUInt32(values[i]); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         [Arguments(1)] | 
 |         [Arguments(2)] | 
 |         [Arguments(3)] | 
 |         [Arguments(4)] | 
 |         [Arguments(5)] | 
 |         [Arguments(6)] | 
 |         [Arguments(7)] | 
 |         [Arguments(8)] | 
 |         [Arguments(9)] | 
 |         [Arguments(10)] | 
 |         public void WriteRawVarint64_CodedOutputStream(int encodedSize) | 
 |         { | 
 |             var values = varint64Values[encodedSize]; | 
 |             var cos = new CodedOutputStream(outputBuffer); | 
 |             for (int i = 0; i < values.Length; i++) | 
 |             { | 
 |                 cos.WriteRawVarint64(values[i]); | 
 |             } | 
 |             cos.Flush(); | 
 |             cos.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         [Arguments(1)] | 
 |         [Arguments(2)] | 
 |         [Arguments(3)] | 
 |         [Arguments(4)] | 
 |         [Arguments(5)] | 
 |         [Arguments(6)] | 
 |         [Arguments(7)] | 
 |         [Arguments(8)] | 
 |         [Arguments(9)] | 
 |         [Arguments(10)] | 
 |         public void WriteRawVarint64_WriteContext(int encodedSize) | 
 |         { | 
 |             var values = varint64Values[encodedSize]; | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             for (int i = 0; i < values.Length; i++) | 
 |             { | 
 |                 ctx.WriteUInt64(values[i]); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteFixed32_CodedOutputStream() | 
 |         { | 
 |             const int encodedSize = sizeof(uint); | 
 |             var cos = new CodedOutputStream(outputBuffer); | 
 |             for (int i = 0; i < BytesToWrite / encodedSize; i++) | 
 |             { | 
 |                 cos.WriteFixed32(12345); | 
 |             } | 
 |             cos.Flush(); | 
 |             cos.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteFixed32_WriteContext() | 
 |         { | 
 |             const int encodedSize = sizeof(uint); | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             for (uint i = 0; i < BytesToWrite / encodedSize; i++) | 
 |             { | 
 |                 ctx.WriteFixed32(12345); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteFixed64_CodedOutputStream() | 
 |         { | 
 |             const int encodedSize = sizeof(ulong); | 
 |             var cos = new CodedOutputStream(outputBuffer); | 
 |             for(int i = 0; i < BytesToWrite / encodedSize; i++) | 
 |             { | 
 |                 cos.WriteFixed64(123456789); | 
 |             } | 
 |             cos.Flush(); | 
 |             cos.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteFixed64_WriteContext() | 
 |         { | 
 |             const int encodedSize = sizeof(ulong); | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             for (uint i = 0; i < BytesToWrite / encodedSize; i++) | 
 |             { | 
 |                 ctx.WriteFixed64(123456789); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteRawTag_OneByte_WriteContext() | 
 |         { | 
 |             const int encodedSize = 1; | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             for (uint i = 0; i < BytesToWrite / encodedSize; i++) | 
 |             { | 
 |                 ctx.WriteRawTag(16); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteRawTag_TwoBytes_WriteContext() | 
 |         { | 
 |             const int encodedSize = 2; | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             for (uint i = 0; i < BytesToWrite / encodedSize; i++) | 
 |             { | 
 |                 ctx.WriteRawTag(137, 6); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteRawTag_ThreeBytes_WriteContext() | 
 |         { | 
 |             const int encodedSize = 3; | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             for (uint i = 0; i < BytesToWrite / encodedSize; i++) | 
 |             { | 
 |                 ctx.WriteRawTag(160, 131, 1); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void Baseline_WriteContext() | 
 |         { | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             ctx.state.position = outputBuffer.Length; | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteRawFloat_CodedOutputStream() | 
 |         { | 
 |             var cos = new CodedOutputStream(outputBuffer); | 
 |             foreach (var value in floatValues) | 
 |             { | 
 |                 cos.WriteFloat(value); | 
 |             } | 
 |             cos.Flush(); | 
 |             cos.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteRawFloat_WriteContext() | 
 |         { | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             foreach (var value in floatValues) | 
 |             { | 
 |                 ctx.WriteFloat(value); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteRawDouble_CodedOutputStream() | 
 |         { | 
 |             var cos = new CodedOutputStream(outputBuffer); | 
 |             foreach (var value in doubleValues) | 
 |             { | 
 |                 cos.WriteDouble(value); | 
 |             } | 
 |             cos.Flush(); | 
 |             cos.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         public void WriteRawDouble_WriteContext() | 
 |         { | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             foreach (var value in doubleValues) | 
 |             { | 
 |                 ctx.WriteDouble(value); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         [ArgumentsSource(nameof(StringEncodedSizes))] | 
 |         public void WriteString_CodedOutputStream(int encodedSize) | 
 |         { | 
 |             var values = stringValues[encodedSize]; | 
 |             var cos = new CodedOutputStream(outputBuffer); | 
 |             foreach (var value in values) | 
 |             { | 
 |                 cos.WriteString(value); | 
 |             } | 
 |             cos.Flush(); | 
 |             cos.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         [ArgumentsSource(nameof(StringEncodedSizes))] | 
 |         public void WriteString_WriteContext(int encodedSize) | 
 |         { | 
 |             var values = stringValues[encodedSize]; | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             foreach (var value in values) | 
 |             { | 
 |                 ctx.WriteString(value); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         [ArgumentsSource(nameof(NonAsciiStringEncodedSizes))] | 
 |         public void WriteNonAsciiString_CodedOutputStream(int encodedSize) | 
 |         { | 
 |             var values = nonAsciiStringValues[encodedSize]; | 
 |             var cos = new CodedOutputStream(outputBuffer); | 
 |             foreach (var value in values) | 
 |             { | 
 |                 cos.WriteString(value); | 
 |             } | 
 |             cos.Flush(); | 
 |             cos.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         [ArgumentsSource(nameof(NonAsciiStringEncodedSizes))] | 
 |         public void WriteNonAsciiString_WriteContext(int encodedSize) | 
 |         { | 
 |             var values = nonAsciiStringValues[encodedSize]; | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             foreach (var value in values) | 
 |             { | 
 |                 ctx.WriteString(value); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         [ArgumentsSource(nameof(StringEncodedSizes))] | 
 |         public void WriteBytes_CodedOutputStream(int encodedSize) | 
 |         { | 
 |             var values = byteStringValues[encodedSize]; | 
 |             var cos = new CodedOutputStream(outputBuffer); | 
 |             foreach (var value in values) | 
 |             { | 
 |                 cos.WriteBytes(value); | 
 |             } | 
 |             cos.Flush(); | 
 |             cos.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         [Benchmark] | 
 |         [ArgumentsSource(nameof(StringEncodedSizes))] | 
 |         public void WriteBytes_WriteContext(int encodedSize) | 
 |         { | 
 |             var values = byteStringValues[encodedSize]; | 
 |             var span = new Span<byte>(outputBuffer); | 
 |             WriteContext.Initialize(ref span, out WriteContext ctx); | 
 |             foreach (var value in values) | 
 |             { | 
 |                 ctx.WriteBytes(value); | 
 |             } | 
 |             ctx.Flush(); | 
 |             ctx.CheckNoSpaceLeft(); | 
 |         } | 
 |  | 
 |         private static uint[] CreateRandomVarints32(Random random, int valueCount, int encodedSize) | 
 |         { | 
 |             var result = new uint[valueCount]; | 
 |             for (int i = 0; i < valueCount; i++) | 
 |             { | 
 |                 result[i] = (uint) ParseRawPrimitivesBenchmark.RandomUnsignedVarint(random, encodedSize, true); | 
 |             } | 
 |             return result; | 
 |         } | 
 |  | 
 |         private static ulong[] CreateRandomVarints64(Random random, int valueCount, int encodedSize) | 
 |         {             | 
 |             var result = new ulong[valueCount]; | 
 |             for (int i = 0; i < valueCount; i++) | 
 |             { | 
 |                 result[i] = ParseRawPrimitivesBenchmark.RandomUnsignedVarint(random, encodedSize, false); | 
 |             } | 
 |             return result; | 
 |         } | 
 |  | 
 |         private static float[] CreateRandomFloats(Random random, int valueCount) | 
 |         { | 
 |             var result = new float[valueCount]; | 
 |             for (int i = 0; i < valueCount; i++) | 
 |             { | 
 |                 result[i] = (float)random.NextDouble(); | 
 |             } | 
 |             return result; | 
 |         } | 
 |  | 
 |         private static double[] CreateRandomDoubles(Random random, int valueCount) | 
 |         { | 
 |             var result = new double[valueCount]; | 
 |             for (int i = 0; i < valueCount; i++) | 
 |             { | 
 |                 result[i] = random.NextDouble(); | 
 |             } | 
 |             return result; | 
 |         } | 
 |  | 
 |         private static string[] CreateStrings(int valueCount, int encodedSize) | 
 |         { | 
 |             var str = ParseRawPrimitivesBenchmark.CreateStringWithEncodedSize(encodedSize); | 
 |  | 
 |             var result = new string[valueCount]; | 
 |             for (int i = 0; i < valueCount; i++) | 
 |             { | 
 |                 result[i] = str; | 
 |             } | 
 |             return result; | 
 |         } | 
 |  | 
 |         private static string[] CreateNonAsciiStrings(int valueCount, int encodedSize) | 
 |         { | 
 |             var str = ParseRawPrimitivesBenchmark.CreateNonAsciiStringWithEncodedSize(encodedSize); | 
 |  | 
 |             var result = new string[valueCount]; | 
 |             for (int i = 0; i < valueCount; i++) | 
 |             { | 
 |                 result[i] = str; | 
 |             } | 
 |             return result; | 
 |         } | 
 |  | 
 |         private static ByteString[] CreateByteStrings(int valueCount, int encodedSize) | 
 |         { | 
 |             var str = ParseRawPrimitivesBenchmark.CreateStringWithEncodedSize(encodedSize); | 
 |  | 
 |             var result = new ByteString[valueCount]; | 
 |             for (int i = 0; i < valueCount; i++) | 
 |             { | 
 |                 result[i] = ByteString.CopyFrom(Encoding.UTF8.GetBytes(str)); | 
 |             } | 
 |             return result; | 
 |         } | 
 |     } | 
 | } |