Skip to content

Instantly share code, notes, and snippets.

@neon-sunset
Last active September 5, 2022 23:59
Show Gist options
  • Select an option

  • Save neon-sunset/d936e54bfec1031d1628c338be4a7f5d to your computer and use it in GitHub Desktop.

Select an option

Save neon-sunset/d936e54bfec1031d1628c338be4a7f5d to your computer and use it in GitHub Desktop.
Lightweight HTTP throughput benchmark template for high-load scenarios. Run dotnet new console, dotnet add package RangeExtensions, copy template to Program.cs and then run with dotnet run -c release.
using System.Diagnostics;
// Set your own execution time here.
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
// dotnet add package RangeExtensions
// Feel free to spawn an arbitrary (within reason) count of workers to simulate desired concurrency.
// Keep in mind that HttpClient might reuse HttpMessageHandler's, feel free to tune to your taste.
var workers = (..64) // ..Environment.ProcessorCount is also a good idea for simpler cases.
.AsEnumerable()
.Select(i => Execute(i, cts.Token));
var reports = await Task.WhenAll(workers);
var aggregated = reports.Aggregate(default(Report), (seed, r) => seed + r);
// Print all reports.
// Console.WriteLine(string.Join('\n', reports));
Console.WriteLine(aggregated);
static async Task<Report> Execute(int i, CancellationToken ct)
{
Console.WriteLine($"Starting worker {i}!");
uint successful = 0, failed = 0;
double kbTransferred = 0;
using var http = new HttpClient();
// Set your URL here. Keep in mind you might also need to set user-agent to make some CDNs happy.
// You can do it with http.DefaultRequestHeaders.Add("User-Agent", yourUserAgent).
var uri = new Uri("http://localhost:5000/posts?name=example");
var stopwatch = Stopwatch.StartNew();
while (!ct.IsCancellationRequested)
{
var response = await http.GetAsync(uri, CancellationToken.None);
if (response.IsSuccessStatusCode)
{
successful++;
kbTransferred += (response.Content.Headers.ContentLength ?? 0) / 1024D;
}
else
{
failed++;
}
}
Console.WriteLine($"Worker {i} stopped!");
var seconds = stopwatch.Elapsed.TotalSeconds;
return new(
Successful: successful,
Failed: failed,
MegabytesPerSecond: kbTransferred / 1024 / seconds,
RPS: (successful + failed) / seconds;
}
readonly record struct Report(
ulong Successful,
ulong Failed,
double MegabytesPerSecond,
double RPS)
{
public static Report operator +(Report left, Report right) => new(
left.Successful + right.Successful,
left.Failed + right.Failed,
left.MegabytesPerSecond + right.MegabytesPerSecond,
left.RPS + right.RPS);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment