parent
b2c94974b2
commit
886c263a3c
2 changed files with 424 additions and 4 deletions
@ -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…
Reference in new issue