initial version of write benchmarks

pull/7576/head
Jan Tattermusch 5 years ago
parent b2c94974b2
commit 886c263a3c
  1. 14
      csharp/src/Google.Protobuf.Benchmarks/ParseRawPrimitivesBenchmark.cs
  2. 414
      csharp/src/Google.Protobuf.Benchmarks/WriteRawPrimitivesBenchmark.cs

@ -337,7 +337,7 @@ namespace Google.Protobuf.Benchmarks
CodedOutputStream cos = new CodedOutputStream(ms);
for (int i = 0; i < valueCount + paddingValueCount; i++)
{
cos.WriteUInt64(RandomUnsignedVarint(random, encodedSize));
cos.WriteUInt64(RandomUnsignedVarint(random, encodedSize, false));
}
cos.Flush();
var buffer = ms.ToArray();
@ -386,11 +386,11 @@ namespace Google.Protobuf.Benchmarks
/// <summary>
/// Generate a random value that will take exactly "encodedSize" bytes when varint-encoded.
/// </summary>
private static ulong RandomUnsignedVarint(Random random, int encodedSize)
public static ulong RandomUnsignedVarint(Random random, int encodedSize, bool fitsIn32Bits)
{
Span<byte> randomBytesBuffer = stackalloc byte[8];
if (encodedSize < 1 || encodedSize > 10)
if (encodedSize < 1 || encodedSize > 10 || (fitsIn32Bits && encodedSize > 5))
{
throw new ArgumentException("Illegal encodedSize value requested", nameof(encodedSize));
}
@ -406,6 +406,12 @@ namespace Google.Protobuf.Benchmarks
ulong bitmask = encodedSize < 10 ? ((1UL << (encodedSize * bitsPerByte)) - 1) : ulong.MaxValue;
result = randomValue & bitmask;
if (fitsIn32Bits)
{
// make sure the resulting value is representable by a uint.
result &= uint.MaxValue;
}
if (encodedSize == 10)
{
// for 10-byte values the highest bit always needs to be set (7*9=63)
@ -443,7 +449,7 @@ namespace Google.Protobuf.Benchmarks
return buffer;
}
private static string CreateStringWithEncodedSize(int encodedSize)
public static string CreateStringWithEncodedSize(int encodedSize)
{
var str = new string('a', encodedSize);
while (CodedOutputStream.ComputeStringSize(str) > encodedSize)

@ -0,0 +1,414 @@
#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, 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 };
[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));
}
}
// 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);
foreach (var value in values)
{
cos.WriteRawVarint32(value);
}
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);
foreach (var value in values)
{
ctx.WriteUInt32(value);
}
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);
foreach (var value in values)
{
cos.WriteRawVarint64(value);
}
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);
foreach (var value in values)
{
ctx.WriteUInt64(value);
}
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(uint);
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 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(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 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;
}
}
}
Loading…
Cancel
Save