Skip to content

Instantly share code, notes, and snippets.

@amoerie
Created November 24, 2021 09:27
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 amoerie/76d9455a7c5d022907c3377743d532da to your computer and use it in GitHub Desktop.
Save amoerie/76d9455a7c5d022907c3377743d532da to your computer and use it in GitHub Desktop.
NetworkStream vs BufferedStream benchmarks
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
namespace Gists
{
[MemoryDiagnoser]
public class NetworkBenchmarks
{
private TcpListener _listener;
[Params(10000)]
public int NumWrites { get; set; }
[GlobalSetup]
public void GlobalSetup()
{
_listener = new TcpListener(IPAddress.Loopback, 0);
_listener.Start(1);
}
[GlobalCleanup]
public void GlobalCleanup()
{
_listener.Stop();
}
[Benchmark]
public async Task Vanilla()
{
await RunWithConnectedNetworkStreamsAsync(async (server, client) =>
{
await TransferAsync(server, client);
});
}
[Benchmark]
public async Task BufferedClient()
{
await RunWithConnectedNetworkStreamsAsync(async (server, client) =>
{
using (var bufferedClient = new BufferedStream(client))
await TransferAsync(server, bufferedClient);
});
}
[Benchmark]
public async Task BufferedServer()
{
await RunWithConnectedNetworkStreamsAsync(async (server, client) =>
{
using (var bufferedServer = new BufferedStream(server))
await TransferAsync(bufferedServer, client);
});
}
[Benchmark]
public async Task BufferedClientAndServer()
{
await RunWithConnectedNetworkStreamsAsync(async (server, client) =>
{
using (var bufferedServer = new BufferedStream(server))
using (var bufferedClient = new BufferedStream(client))
await TransferAsync(bufferedServer, bufferedClient);
});
}
Task TransferAsync(Stream server, Stream client)
{
return Task.WhenAll(
Task.Run(async () =>
{
for (int i = 0; i < NumWrites; i++)
server.WriteByte((byte)i);
await server.FlushAsync();
}),
Task.Run(() =>
{
for (int i = 0; i < NumWrites; i++)
client.ReadByte();
}));
}
private async Task RunWithConnectedNetworkStreamsAsync(Func<NetworkStream, NetworkStream, Task> func)
{
var clientEndpoint = (IPEndPoint)_listener.LocalEndpoint;
using (var client = new TcpClient(clientEndpoint.AddressFamily))
{
Task<TcpClient> remoteTask = _listener.AcceptTcpClientAsync();
Task clientConnectTask = client.ConnectAsync(clientEndpoint.Address, clientEndpoint.Port);
await Task.WhenAll(remoteTask, clientConnectTask);
using (TcpClient remote = remoteTask.Result)
using (NetworkStream serverStream = remote.GetStream())
using (NetworkStream clientStream = client.GetStream())
{
await func(serverStream, clientStream);
}
}
}
}
}
@amoerie
Copy link
Author

amoerie commented Nov 24, 2021

Produces:

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.22000
Intel Core i7-8700K CPU 3.70GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=5.0.403
  [Host]     : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT
  Job-GUSZAQ : .NET Core 3.1.21 (CoreCLR 4.700.21.51404, CoreFX 4.700.21.51508), X64 RyuJIT

InvocationCount=128  MaxIterationCount=25  MaxWarmupIterationCount=10  
UnrollFactor=16  
Method NumWrites Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
Vanilla 10000 40,934.8 us 285.93 us 253.47 us - - - 3.7 KB
BufferedClient 10000 43,534.5 us 1,837.64 us 2,453.19 us - - - 7.81 KB
BufferedServer 10000 7,782.4 us 143.95 us 134.65 us - - - 8.56 KB
BufferedClientAndServer 10000 282.9 us 2.86 us 2.68 us - - - 13.09 KB

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