From d7c1fab00b231e8d99686c7f5841574fbeb82963 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Tue, 21 Apr 2020 16:31:32 +0200 Subject: [PATCH] add benchmarks for parsing string and bytes --- .../ParseRawPrimitivesBenchmark.cs | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs b/csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs index 8d3e13aef4..863e74dc49 100644 --- a/csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs +++ b/csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs @@ -52,8 +52,13 @@ namespace Google.Protobuf.Benchmarks byte[] floatInputBuffer; byte[] fixedIntInputBuffer; + // key is the encodedSize of string values + Dictionary stringInputBuffers; + Random random = new Random(417384220); // random but deterministic seed + public IEnumerable StringEncodedSizes => new[] { 1, 4, 10, 105, 10080 }; + [GlobalSetup] public void GlobalSetup() { @@ -71,6 +76,13 @@ namespace Google.Protobuf.Benchmarks doubleInputBuffer = CreateBufferWithRandomDoubles(random, BytesToParse / sizeof(double), paddingValueCount); floatInputBuffer = CreateBufferWithRandomFloats(random, BytesToParse / sizeof(float), paddingValueCount); fixedIntInputBuffer = CreateBufferWithRandomData(random, BytesToParse / sizeof(long), sizeof(long), paddingValueCount); + + stringInputBuffers = new Dictionary(); + foreach(var encodedSize in StringEncodedSizes) + { + byte[] buffer = CreateBufferWithStrings(BytesToParse / encodedSize, encodedSize, encodedSize < 10 ? 10 : 1 ); + stringInputBuffers.Add(encodedSize, buffer); + } } // Total number of bytes that each benchmark will parse. @@ -262,6 +274,58 @@ namespace Google.Protobuf.Benchmarks return sum; } + [Benchmark] + [ArgumentsSource(nameof(StringEncodedSizes))] + public int ParseString_CodedInputStream(int encodedSize) + { + CodedInputStream cis = new CodedInputStream(stringInputBuffers[encodedSize]); + int sum = 0; + for (int i = 0; i < BytesToParse / encodedSize; i++) + { + sum += cis.ReadString().Length; + } + return sum; + } + + [Benchmark] + [ArgumentsSource(nameof(StringEncodedSizes))] + public int ParseString_ParseContext(int encodedSize) + { + InitializeParseContext(stringInputBuffers[encodedSize], out ParseContext ctx); + int sum = 0; + for (int i = 0; i < BytesToParse / encodedSize; i++) + { + sum += ctx.ReadString().Length; + } + return sum; + } + + [Benchmark] + [ArgumentsSource(nameof(StringEncodedSizes))] + public int ParseBytes_CodedInputStream(int encodedSize) + { + CodedInputStream cis = new CodedInputStream(stringInputBuffers[encodedSize]); + int sum = 0; + for (int i = 0; i < BytesToParse / encodedSize; i++) + { + sum += cis.ReadBytes().Length; + } + return sum; + } + + [Benchmark] + [ArgumentsSource(nameof(StringEncodedSizes))] + public int ParseBytes_ParseContext(int encodedSize) + { + InitializeParseContext(stringInputBuffers[encodedSize], out ParseContext ctx); + int sum = 0; + for (int i = 0; i < BytesToParse / encodedSize; i++) + { + sum += ctx.ReadBytes().Length; + } + return sum; + } + private static void InitializeParseContext(byte[] buffer, out ParseContext ctx) { ParseContext.Initialize(new ReadOnlySequence(buffer), out ctx); @@ -358,5 +422,40 @@ namespace Google.Protobuf.Benchmarks } return result; } + + private static byte[] CreateBufferWithStrings(int valueCount, int encodedSize, int paddingValueCount) + { + var str = CreateStringWithEncodedSize(encodedSize); + + MemoryStream ms = new MemoryStream(); + CodedOutputStream cos = new CodedOutputStream(ms); + for (int i = 0; i < valueCount + paddingValueCount; i++) + { + cos.WriteString(str); + } + cos.Flush(); + var buffer = ms.ToArray(); + + if (buffer.Length != encodedSize * (valueCount + paddingValueCount)) + { + throw new InvalidOperationException($"Unexpected output buffer length {buffer.Length}"); + } + return buffer; + } + + private static string CreateStringWithEncodedSize(int encodedSize) + { + var str = new string('a', encodedSize); + while (CodedOutputStream.ComputeStringSize(str) > encodedSize) + { + str = str.Substring(1); + } + + if (CodedOutputStream.ComputeStringSize(str) != encodedSize) + { + throw new InvalidOperationException($"Generated string with wrong encodedSize"); + } + return str; + } } }