Skip to content

Instantly share code, notes, and snippets.

@benaadams
Last active December 9, 2018 04:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benaadams/77b3cba47261beadc94cc9fcb6d5ebd4 to your computer and use it in GitHub Desktop.
Save benaadams/77b3cba47261beadc94cc9fcb6d5ebd4 to your computer and use it in GitHub Desktop.
PipeThroughputBenchmark
SET COMPlus_TieredCompilation=0
using System;
using System.Buffers;
using System.IO.Pipelines;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
public class PipeThroughputBenchmark
{
// 128 (min Length) * InnerLoopCount > _memoryPool.MaxBufferSize
private const int InnerLoopCount = 33;
private PipeReader _reader;
private PipeWriter _writer;
private MemoryPool<byte> _memoryPool;
[GlobalSetup]
public void Setup()
{
_memoryPool = KestrelMemoryPool.Create();
var chunkLength = Length / Chunks;
if (chunkLength > _memoryPool.MaxBufferSize)
{
// Parallel test will deadlock if too large (waiting for second Task to complete), so N/A that run
throw new InvalidOperationException();
}
if (Length != chunkLength * Chunks)
{
// Test will deadlock waiting for data so N/A that run
throw new InvalidOperationException();
}
var pipe = new Pipe(new PipeOptions(_memoryPool));
_reader = pipe.Reader;
_writer = pipe.Writer;
}
[Params(1, 2, 4, 8)]
public int Chunks { get; set; }
[Params(128, 256, 1024, 2048, 4096, 8192)]
public int Length { get; set; }
[Benchmark(OperationsPerInvoke = InnerLoopCount)]
public Task Parse_ParallelAsync()
{
return Parse_ParallelAsyncImpl();
}
private Task Parse_ParallelAsyncImpl()
{
var writing = Task.Run(async () =>
{
var chunks = Chunks;
var chunkLength = Length / chunks;
for (int i = 0; i < InnerLoopCount; i++)
{
for (var c = 0; c < chunks; c++)
{
_writer.GetMemory(chunkLength);
_writer.Advance(chunkLength);
await _writer.FlushAsync();
}
}
});
var reading = Task.Run(async () =>
{
long remaining = InnerLoopCount * Length;
while (remaining != 0)
{
var result = await _reader.ReadAsync();
remaining -= result.Buffer.Length;
_reader.AdvanceTo(result.Buffer.End, result.Buffer.End);
}
});
return Task.WhenAll(writing, reading);
}
[Benchmark(OperationsPerInvoke = InnerLoopCount)]
public Task Parse_SequentialAsync()
{
return Parse_SequentialAsyncImpl();
}
private async Task Parse_SequentialAsyncImpl()
{
var chunks = Chunks;
var chunkLength = Length / chunks;
for (int i = 0; i < InnerLoopCount; i++)
{
for (var c = 0; c < chunks; c++)
{
_writer.GetMemory(chunkLength);
_writer.Advance(chunkLength);
await _writer.FlushAsync();
}
var result = await _reader.ReadAsync();
_reader.AdvanceTo(result.Buffer.End, result.Buffer.End);
}
}
[GlobalCleanup]
public void Cleanup()
{
_memoryPool.Dispose();
}
public static void Main(string[] args)
{
var summary = BenchmarkSwitcher.FromAssembly(typeof(PipeThroughputBenchmark).Assembly).Run(args);
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.3" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="System.IO.Pipelines" Version="4.5.2" />
</ItemGroup>
</Project>
@benaadams
Copy link
Author

benaadams commented Dec 8, 2018

                Method | Chunks | Length |       Mean |      Error |     StdDev |
---------------------- |------- |------- |-----------:|-----------:|-----------:|
   Parse_ParallelAsync |      1 |    128 |   615.0 ns |  3.1738 ns |  2.9688 ns |
 Parse_SequentialAsync |      1 |    128 |   322.6 ns |  0.5409 ns |  0.4795 ns |
   Parse_ParallelAsync |      1 |    256 |   605.8 ns |  1.6088 ns |  1.5048 ns |
 Parse_SequentialAsync |      1 |    256 |   321.8 ns |  0.6478 ns |  0.5743 ns |
   Parse_ParallelAsync |      1 |   1024 |   663.3 ns |  1.6789 ns |  1.5705 ns |
 Parse_SequentialAsync |      1 |   1024 |   322.2 ns |  0.7686 ns |  0.7190 ns |
   Parse_ParallelAsync |      1 |   2048 |   809.4 ns |  4.0722 ns |  3.8091 ns |
 Parse_SequentialAsync |      1 |   2048 |   323.3 ns |  1.5330 ns |  1.4340 ns |
   Parse_ParallelAsync |      1 |   4096 | 1,043.7 ns |  3.1687 ns |  2.9640 ns |
 Parse_SequentialAsync |      1 |   4096 |   329.6 ns |  0.5404 ns |  0.4512 ns |
   Parse_ParallelAsync |      1 |   8192 |         NA |         NA |         NA |
 Parse_SequentialAsync |      1 |   8192 |         NA |         NA |         NA |
   Parse_ParallelAsync |      2 |    128 | 1,043.0 ns |  2.2123 ns |  1.9611 ns |
 Parse_SequentialAsync |      2 |    128 |   436.3 ns |  2.3291 ns |  2.1786 ns |
   Parse_ParallelAsync |      2 |    256 | 1,033.3 ns |  4.1923 ns |  3.9215 ns |
 Parse_SequentialAsync |      2 |    256 |   437.4 ns | 10.8902 ns | 10.1867 ns |
   Parse_ParallelAsync |      2 |   1024 | 1,064.3 ns |  2.8559 ns |  2.5317 ns |
 Parse_SequentialAsync |      2 |   1024 |   434.6 ns |  1.8056 ns |  1.5078 ns |
   Parse_ParallelAsync |      2 |   2048 | 1,136.8 ns |  3.4331 ns |  3.2113 ns |
 Parse_SequentialAsync |      2 |   2048 |   428.5 ns |  2.2958 ns |  2.0352 ns |
   Parse_ParallelAsync |      2 |   4096 | 1,306.6 ns |  4.1388 ns |  3.8714 ns |
 Parse_SequentialAsync |      2 |   4096 |   434.1 ns |  1.7415 ns |  1.4542 ns |
   Parse_ParallelAsync |      2 |   8192 | 1,659.6 ns |  3.9254 ns |  3.6718 ns |
 Parse_SequentialAsync |      2 |   8192 |   498.7 ns |  1.3643 ns |  1.2094 ns |
   Parse_ParallelAsync |      4 |    128 | 1,658.4 ns | 10.6838 ns |  9.4709 ns |
 Parse_SequentialAsync |      4 |    128 |   670.2 ns |  1.8434 ns |  1.6341 ns |
   Parse_ParallelAsync |      4 |    256 | 1,644.4 ns |  6.7991 ns |  5.3083 ns |
 Parse_SequentialAsync |      4 |    256 |   692.3 ns | 13.7884 ns | 12.8977 ns |
   Parse_ParallelAsync |      4 |   1024 | 1,771.7 ns | 33.7018 ns | 36.0606 ns |
 Parse_SequentialAsync |      4 |   1024 |   653.0 ns |  2.6855 ns |  2.0966 ns |
   Parse_ParallelAsync |      4 |   2048 | 1,721.1 ns | 21.0045 ns | 19.6476 ns |
 Parse_SequentialAsync |      4 |   2048 |   665.9 ns |  2.1371 ns |  1.6685 ns |
   Parse_ParallelAsync |      4 |   4096 | 1,872.4 ns | 13.2456 ns | 12.3899 ns |
 Parse_SequentialAsync |      4 |   4096 |   649.4 ns |  2.7504 ns |  2.5727 ns |
   Parse_ParallelAsync |      4 |   8192 | 2,189.3 ns |  5.1524 ns |  4.8195 ns |
 Parse_SequentialAsync |      4 |   8192 |   728.4 ns |  4.1580 ns |  3.8894 ns |
   Parse_ParallelAsync |      8 |    128 | 2,797.3 ns | 16.9029 ns | 15.8110 ns |
 Parse_SequentialAsync |      8 |    128 | 1,093.9 ns |  5.8144 ns |  5.4388 ns |
   Parse_ParallelAsync |      8 |    256 | 2,767.8 ns | 27.9809 ns | 26.1734 ns |
 Parse_SequentialAsync |      8 |    256 | 1,075.9 ns |  5.6136 ns |  5.2510 ns |
   Parse_ParallelAsync |      8 |   1024 | 2,805.9 ns | 73.2250 ns | 71.9168 ns |
 Parse_SequentialAsync |      8 |   1024 | 1,089.7 ns |  2.8515 ns |  2.6673 ns |
   Parse_ParallelAsync |      8 |   2048 | 2,822.3 ns | 21.9266 ns | 20.5101 ns |
 Parse_SequentialAsync |      8 |   2048 | 1,082.8 ns |  7.1544 ns |  6.6922 ns |
   Parse_ParallelAsync |      8 |   4096 | 3,052.4 ns | 29.3138 ns | 27.4201 ns |
 Parse_SequentialAsync |      8 |   4096 | 1,098.1 ns |  8.8584 ns |  8.2861 ns |
   Parse_ParallelAsync |      8 |   8192 | 3,335.2 ns | 30.3906 ns | 28.4274 ns |
 Parse_SequentialAsync |      8 |   8192 | 1,157.9 ns |  1.7976 ns |  1.5935 ns |

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment