75 lines
5.4 KiB
C#
75 lines
5.4 KiB
C#
using System.Diagnostics;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text.Json;
|
|
using Cysharp.Threading.Tasks;
|
|
using ShrinkNetwork;
|
|
|
|
public static class TcpBenchmarks
|
|
{
|
|
public static async Task Run(string output)
|
|
{
|
|
var rows = new List<object>();
|
|
foreach (var connections in new[] { 1, 32, 256 })
|
|
{
|
|
var probe = new TcpListener(IPAddress.Loopback, 0); probe.Start(); var port = ((IPEndPoint)probe.LocalEndpoint).Port; probe.Stop();
|
|
var transport = new ShrinkTcpServerTransport(IPAddress.Loopback, port);
|
|
var server = Service();
|
|
server.RegisterRequestHandler<NetworkTransportTests.Request, NetworkTransportTests.Response>((_, request) => UniTask.FromResult(new NetworkTransportTests.Response { Text = request.Text }));
|
|
server.BindTransport(transport);
|
|
var clients = new List<(ShrinkNetworkService service, ShrinkTcpClientTransport transport, ShrinkNetworkSession session)>();
|
|
long notifications = 0;
|
|
try
|
|
{
|
|
for (var i = 0; i < connections; i++)
|
|
{
|
|
var client = Service(); var link = new ShrinkTcpClientTransport("127.0.0.1", port);
|
|
var connected = new TaskCompletionSource<ShrinkNetworkSession>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
client.OnSessionConnected += s => connected.TrySetResult(s);
|
|
client.RegisterHandler<NetworkTransportTests.Request>((_, _) => { Interlocked.Increment(ref notifications); return UniTask.CompletedTask; });
|
|
client.BindTransport(link); clients.Add((client, link, await connected.Task.WaitAsync(TimeSpan.FromSeconds(10))));
|
|
}
|
|
foreach (var bytes in new[] { 64, 1024, 32768 })
|
|
{
|
|
var samples = new List<double>();
|
|
var message = new NetworkTransportTests.Request { Text = new string('t', bytes) };
|
|
var allocated = GC.GetTotalAllocatedBytes(true); var elapsed = Stopwatch.StartNew();
|
|
for (var i = 0; i < 256; i++)
|
|
{
|
|
var client = clients[i % clients.Count]; var start = Stopwatch.GetTimestamp();
|
|
var response = await client.service.CallAsync<NetworkTransportTests.Request, NetworkTransportTests.Response>(client.session, message).AsTask().WaitAsync(TimeSpan.FromSeconds(10));
|
|
if (response.Text.Length != bytes) throw new InvalidDataException("RPC content mismatch");
|
|
samples.Add(Stopwatch.GetElapsedTime(start).TotalMicroseconds);
|
|
}
|
|
samples.Sort();
|
|
rows.Add(new { scenario = "tcp/rpc", connections, bytes, operations = 256, throughputPerSecond = 256 / elapsed.Elapsed.TotalSeconds,
|
|
p50Microseconds = samples[128], p95Microseconds = samples[243], p99Microseconds = samples[253], processAllocatedBytes = GC.GetTotalAllocatedBytes(true) - allocated });
|
|
var payload = new ShrinkJsonNetworkSerializer().Serialize(message); var before = Interlocked.Read(ref notifications); elapsed.Restart();
|
|
foreach (var session in server.Sessions.Values) await server.SendSerializedAsync(session, typeof(NetworkTransportTests.Request), payload);
|
|
while (Interlocked.Read(ref notifications) - before < connections)
|
|
{ if (elapsed.Elapsed > TimeSpan.FromSeconds(10)) throw new TimeoutException("Broadcast drain"); await Task.Delay(1); }
|
|
rows.Add(new { scenario = "tcp/broadcast", connections, bytes, elapsedMilliseconds = elapsed.Elapsed.TotalMilliseconds, delivered = Interlocked.Read(ref notifications) - before });
|
|
}
|
|
using var queue = new ShrinkNetworkDispatchQueue(32, byteCapacity: 32768, perSessionCapacity: 2);
|
|
server.DispatchScheduler = queue;
|
|
var attempts = connections * 8;
|
|
for (var i = 0; i < attempts; i++)
|
|
{
|
|
var c = clients[i % clients.Count];
|
|
if (c.service.Sessions.Count != 0) try { await c.service.SendAsync(c.session, new NetworkTransportTests.Request { Text = new string('s', 1024) }); } catch (IOException) { }
|
|
}
|
|
await Task.Delay(100);
|
|
var beforePump = queue.CaptureDiagnostics(); await queue.PumpAsync(32, 8192, TimeSpan.FromMilliseconds(2));
|
|
rows.Add(new { scenario = "tcp/slow-consumer-burst", connections, attempts, beforePump, afterPump = queue.CaptureDiagnostics(), metrics = server.GetDiagnosticsSnapshot() });
|
|
}
|
|
finally { foreach (var c in clients) c.transport.Stop(); transport.Stop(); }
|
|
Console.WriteLine("TCP completed: " + connections);
|
|
}
|
|
await File.WriteAllTextAsync(output, JsonSerializer.Serialize(new { runtime = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription, host = "Windows loopback TCP; sequential round-trip samples, multiple real connections", rows }, new JsonSerializerOptions { WriteIndented = true }));
|
|
}
|
|
private static ShrinkNetworkService Service()
|
|
{
|
|
var service = new ShrinkNetworkService(); service.RegisterMessage<NetworkTransportTests.Request>(1); service.RegisterMessage<NetworkTransportTests.Response>(2); return service;
|
|
}
|
|
}
|