Two C# frameworks walk into a benchmark. One is the industry standard backed by Microsoft. The other is a scrappy indie framework most .NET developers have never heard of. You'd expect a blowout — and you'd be right. You just might be wrong about who gets blown out where.
HttpArena recently added both GenHTTP and ASP.NET Minimal APIs to their benchmark suite, and the results tell a story that's way more interesting than "Microsoft wins." Let's dig in.
The Contenders
ASP.NET Minimal APIs needs no introduction. It's Microsoft's lightweight API framework running on Kestrel, the battle-tested HTTP server that powers half the internet's .NET workloads. Minimal APIs strip away controllers and give you app.MapGet("/route", handler) — clean, fast, no ceremony.
GenHTTP is a modular, embeddable C# web server that runs its own HTTP engine. It layers on abstractions — layouts, services, concerns, resource methods — giving you a higher-level programming model. Think "convention over configuration" but for HTTP servers.
Both target .NET 10. Both speak C#. Same runtime, same GC, same JIT. So any performance delta comes down to the framework itself.
Let the games begin.
Round 1: Baseline — The 10x Gap Nobody Expected
| Concurrency | GenHTTP | ASP.NET Minimal |
|---|---|---|
| 512 | 49,507 req/s | 495,831 req/s |
| 4,096 | 48,551 req/s | 459,783 req/s |
| 16,384 | 22,169 req/s | 353,394 req/s |
Yeah. Read that again. ASP.NET is doing 10x the requests per second on a simple GET-with-query-params endpoint.
This is where GenHTTP pays the tax for its abstraction layer. Look at the code — ASP.NET's handler is a direct IResult return. GenHTTP routes through a LayoutBuilder, resolves a ServiceResource, matches a ResourceMethod attribute, and wraps things in a Concern pipeline. Every request walks through that entire abstraction stack.
Is it elegant? Sure. Is it 350,000 requests per second worth of overhead? Also yes.
To be fair, ~50K req/s is still plenty fast for most real applications. But if your use case is "handle a simple request as fast as physically possible," ASP.NET Minimal isn't even trying and it's lapping GenHTTP.
Round 2: Pipelined — Wait, They're Tied?
| Concurrency | GenHTTP | ASP.NET Minimal |
|---|---|---|
| 512 | 12,349,888 req/s | 12,790,124 req/s |
| 4,096 | 13,148,857 req/s | 13,748,303 req/s |
| 16,384 | 10,999,584 req/s | 12,741,100 req/s |
Thirteen. Million. Requests per second. From both of them.
The pipelined test is the great equalizer. When you shove requests down a persistent connection as fast as TCP allows, you're basically benchmarking the I/O layer, not the framework. And GenHTTP, despite its abstraction overhead, keeps pace with ASP.NET within ~15%.
This tells us something important: GenHTTP has solid I/O fundamentals. The bottleneck in baseline isn't bad networking code — it's the request processing pipeline on top. Good bones, heavy coat.
Round 3: Upload — The 23GB Elephant in the Room
| Concurrency | GenHTTP (req/s / RAM) | ASP.NET Minimal (req/s / RAM) |
|---|---|---|
| 64 | 609 / 429MB | 184 / 23.2GB |
| 256 | 630 / 591MB | 187 / 24.1GB |
| 512 | 622 / 862MB | 160 / 22.7GB |
I need you to look at this table and understand what you're seeing. ASP.NET Minimal is using twenty-three gigabytes of RAM to handle file uploads at 160 requests per second. GenHTTP handles nearly 4x the throughput while sipping under a gig.
What is happening here? Let's look at the code.
ASP.NET's upload handler:
public static async Task<IResult> Upload(HttpRequest req)
{
using var ms = new MemoryStream();
await req.Body.CopyToAsync(ms);
return Results.Text(ms.Length.ToString());
}
It copies the entire request body into a MemoryStream. Every upload, fully buffered in RAM. With concurrent uploads, you're stacking multi-megabyte buffers on top of each other. The GC is crying.
GenHTTP's approach:
public ValueTask<long> Compute(Stream input)
{
if (input.CanSeek)
return ValueTask.FromResult(input.Length);
return ComputeManually(input);
}
GenHTTP's engine provides a seekable stream, so it just reads the length directly — zero buffering, zero copying. When running on Kestrel, it falls back to reading chunks through a small buffer. Either way, it's dramatically more memory-efficient.
Now, is this an ASP.NET framework problem? Not exactly — it's a handler implementation choice. You could write a streaming handler in ASP.NET. But the fact that the "obvious" way to handle uploads in Minimal APIs leads to 23GB of RAM usage is... not great. GenHTTP's abstraction actually protects you from this footgun by giving you a smarter stream interface out of the box.
GenHTTP wins this round by a country mile, and it's not even close.
Round 4: JSON Serialization — The Underdog Bites
| Concurrency | GenHTTP | ASP.NET Minimal |
|---|---|---|
| 4,096 | 582,510 req/s | 515,135 req/s |
| 16,384 | 561,453 req/s | 440,034 req/s |
Wait. The framework that was 10x slower on baseline is now beating ASP.NET on JSON serialization? By 13% at 4K connections and 28% at 16K?
The implementations are virtually identical — both deserialize a dataset, transform it, and serialize the response. Same System.Text.Json under the hood. The difference likely comes down to how each framework handles response writing and buffering for larger payloads. GenHTTP's response pipeline may have less overhead when serializing structured data compared to ASP.NET's Results.Json() wrapper.
This is where the "faster framework" narrative breaks down. Performance isn't one number. It's a profile.
Round 5: Compression — GenHTTP Keeps Winning
| Concurrency | GenHTTP | ASP.NET Minimal |
|---|---|---|
| 4,096 | 8,565 req/s | 7,183 req/s |
| 16,384 | 7,572 req/s | 5,949 req/s |
GenHTTP leads by 19-27% on compressed responses. Both use gzip at the fastest compression level, so the delta is in how they pipe compressed bytes to the wire. Another quiet win for the underdog.
Round 6: Noisy Neighbors — Dead Heat
| Concurrency | GenHTTP | ASP.NET Minimal |
|---|---|---|
| 512 | 1,080,435 req/s | 1,110,339 req/s |
| 4,096 | 1,218,298 req/s | 1,210,574 req/s |
| 16,384 | 1,017,992 req/s | 1,007,141 req/s |
Under CPU contention with background noise, they trade punches within 3%. At a million requests per second each, both frameworks handle stress gracefully. No complaints from either corner.
Round 7: Mixed Workload & Database — GenHTTP's Sweet Spot
| Test | GenHTTP | ASP.NET Minimal |
|---|---|---|
| Mixed 4,096c | 7,962 req/s | 6,650 req/s |
| Mixed 16,384c | 9,269 req/s | 6,240 req/s |
| Async DB 512c | 138,179 req/s | 122,812 req/s |
| Async DB 1,024c | 161,488 req/s | 147,924 req/s |
GenHTTP pulls ahead by 20-49% on mixed workloads and 9-12% on async database queries. For realistic API workloads that juggle multiple endpoint types and database access — you know, actual applications — GenHTTP holds its own and then some.
Round 8: HTTP/2 and HTTP/3 — GenHTTP Sits This One Out
| Test | ASP.NET Minimal |
|---|---|
| H2 Baseline 256c | 254,371 req/s |
| H2 Baseline 1,024c | 203,325 req/s |
| H3 Baseline 256c | 41,922 req/s |
ASP.NET supports HTTP/2 and HTTP/3 out of the box via Kestrel's QUIC integration. GenHTTP doesn't participate in these tests — it's HTTP/1.1 only for now. If you need modern protocol support, that's a clear win for ASP.NET.
The Verdict: It's Complicated (Obviously)
If you looked at the baseline numbers alone, you'd conclude ASP.NET Minimal is in a different league. And for raw request routing throughput, it is.
But real applications don't just route requests. They serialize JSON, compress responses, handle file uploads, query databases, and juggle mixed workloads. And in those scenarios, GenHTTP frequently wins — sometimes by significant margins.
Choose ASP.NET Minimal if:
- Raw request throughput is critical
- You need HTTP/2 or HTTP/3 support
- You want the massive .NET ecosystem and tooling
- You're building at scale and need Microsoft's backing
Choose GenHTTP if:
- You want a lightweight, embeddable server
- Memory efficiency matters (looking at you, upload numbers)
- Your workload is JSON-heavy, database-driven, or mixed
- You appreciate a higher-level API that handles footguns for you
The most interesting takeaway? A small indie framework with a handful of contributors is genuinely competitive with — and sometimes faster than — one of the most optimized web stacks on the planet. That's impressive engineering from the GenHTTP team.
Performance isn't a single number. It's a conversation. And this one just got a lot more interesting.
All benchmarks from HttpArena — an open-source, reproducible HTTP benchmark suite. Full source code and methodology available on GitHub.
Top comments (0)