Benchmarks
All numbers come from BenchmarkDotNet with MemoryDiagnoser, on real
hardware. No synthetic inflation.
Environment
| Property | Value |
|---|---|
| Runtime | .NET 10.0.1, Arm64 RyuJIT AdvSIMD |
| OS / CPU | macOS, Apple M5 |
| BenchmarkDotNet | v0.14.0 |
| MediatR | v12.4.1 |
| Mediant | v1.0.0 |
Publish — notification fanout
Where Mediant wins decisively. MediatR allocates a NotificationHandlerExecutor wrapper plus a closure
delegate per handler, per call. Mediant invokes handlers directly — zero wrapper allocation.
| Handlers | Mediant | MediatR v12 | Speed | Memory |
|---|---|---|---|---|
| 1 | 24 ns / 88 B | 44 ns / 288 B | 46% faster | 3.3× less |
| 10 | 69 ns / 376 B | 184 ns / 1,656 B | 63% faster | 4.4× less |
| 50 | 276 ns / 1,656 B | 792 ns / 7,736 B | 65% faster | 4.7× less |
| 100 | 527 ns / 3,256 B | 1,521 ns / 15,336 B | 65% faster | 4.7× less |
Send — command/query pipeline
The hot path: every request goes through Send(). Behaviors are scaled exponentially to show how each
library handles pipeline depth.
| Behaviors | Mediant | MediatR v12 | Speed | Memory |
|---|---|---|---|---|
| 0 | 24 ns / 64 B | 24 ns / 128 B | ~equal | 2× less |
| 1 | 59 ns / 288 B | 57 ns / 368 B | ~equal | 22% less |
| 2 | 75 ns / 424 B | 70 ns / 512 B | ~equal | 17% less |
| 4 | 102 ns / 696 B | 100 ns / 800 B | ~equal | 13% less |
| 8 | 150 ns / 1,240 B | 159 ns / 1,376 B | 6% faster | 10% less |
| 16 | 262 ns / 2,328 B | 277 ns / 2,528 B | 5% faster | 8% less |
| 32 | 478 ns / 4,504 B | 507 ns / 4,832 B | 6% faster | 7% less |
Speed is within noise through 4 behaviors; from 8 up Mediant pulls ahead, and the gap widens with depth. Memory is lower in every scenario.
Query — with a return value
| Scenario | Mediant | MediatR v12 | Speed | Memory |
|---|---|---|---|---|
Query returning Result<int> | 28 ns / 104 B | 28 ns / 200 B | ~equal | 1.9× less |
Note that Mediant returns the richer Result<int> here, while MediatR returns a raw int.
Scorecard
| Category | Benchmarks | Mediant wins | Tie | MediatR wins |
|---|---|---|---|---|
| Send (8+ behaviors) | 3 | 3 | 0 | 0 |
| Send (0–4 behaviors) | 4 | 0 | 4 | 0 |
| Query | 1 | 0 | 1 | 0 |
| Publish | 4 | 4 | 0 | 0 |
| Total | 12 | 7 | 5 | 0 |
Mediant allocates less memory in all 12 benchmarks, and never loses on speed.
Load tests
18 load tests, part of a 300-test suite spanning unit, integration, load and E2E.
Concurrency and throughput
| Test | Scale | Result |
|---|---|---|
Concurrent Send | 10,000 simultaneous | No deadlocks, all succeed |
Concurrent Send + behaviors | 50,000 simultaneous | No deadlocks, all succeed |
Concurrent Query | 5,000 simultaneous | All succeed, correct results |
| Scoped DI (per request) | 10,000 scopes | All independent |
| Mixed operations | 10,000 | All complete cleanly |
Memory and stability
| Test | Scale | Result |
|---|---|---|
| Sequential memory leak | 100,000 requests | < 10 MB growth |
| Sequential + behaviors | 500,000 requests | < 20 MB growth |
| High-cardinality cache keys | 10,000 unique keys | < 20 MB growth |
| Thread-pool exhaustion | 20,000 operations | Pool not depleted |
Resilience
| Test | Scale | Result |
|---|---|---|
| Exceptions under load | 10,000 (33% failing) | All complete, no leaks |
| Cancellation mid-flight | 5,000 + cancel | No hanging tasks |
Re-entrant Send | 1,000 × depth 3 | No deadlocks |
| Parallel fanout | 5,000 × 10 handlers | 50,000 executions, all succeed |
| Streaming consumers | 100 × 1,000 items | All correct |
Sustained latency (10 seconds): p50 < 1 ms, p95 < 5 ms, p99 < 10 ms, throughput > 10,000 req/sec.
Reproduce them
git clone https://github.com/omercelikdev/mediant.gitcd mediant
# BenchmarkDotNet comparison vs MediatRdotnet run -c Release --project tests/Mediant.Benchmarks
# Load testsdotnet test tests/Mediant.LoadTests