Skip to content
github-actions[bot] edited this page Oct 14, 2026 · 210 revisions

Throughput and footprint of Titanium as a reverse / edge proxy and as a decrypting (MITM) proxy, measured on the same harness against YARP, nginx, HAProxy, and Envoy where each OS can run them.

RPS is requests per second (gRPC tables use RPC/s). Numbers are Release builds on matched GitHub-hosted runners: Windows and Linux at 4 vCPU / 16 GiB, macOS at macos-15 Apple Silicon 3-core / 7 GB. We compare products within an OS, not OS against OS, so read within one table — absolute RPS is not comparable across operating systems. Not possible means that product cannot run that path on that OS; Not measured means the path exists but no published number yet.

For pooling knobs and certificate first-visit tuning, see Performance and pooling. Laptop cool A/B tables (not publishable) live on Performance Local Lab.

Why this comparison is fair

  • Same load generator, same origin process, and the same warmup / measure windows (2s / 8s) with the same concurrency ramp (8, 16, 32, 64).
  • Every reverse arm is three OS processes: load generator + origin + proxy. Origin-direct omits the proxy; peers are never in-process with the client.
  • Same runner class per table (windows-latest / ubuntu-latest / macos-15). Laptop numbers are never mixed into these tables.
  • Peers use equivalent TLS/ALPN and streaming-friendly settings on the same loopback shape. HAProxy and Envoy are Linux/macOS only — Not possible on Windows, so their columns are omitted there.
  • MITM (HTTPS decryption with forged certificates) is Titanium-only; peers cannot MITM. The published MITM table is Linux only. It shows Titanium MITM overhead versus its own reverse path on the same wires.
  • Tiny keep-alive GET (~56-byte JSON) is the industry RPS shape (same class as wrk / TechEmpower). It is also real for small JSON APIs and health checks.
  • HTTP/2 and HTTP/3 spread that concurrency across min(concurrency, max(4 × cores, 16)) client connections, the same connection count h2load and wrk fix with -c. On these 4 vCPU runners that is 8, 16, 16, and 16 connections at c = 8, 16, 32, 64.
  • Same-protocol H2↔H2 / H3↔H3 on that shape is Titanium’s best case: with interception off, Titanium copies frames instead of decoding and re-encoding headers (peers do a full HTTP decode). Medals there are not the typical reverse-proxy job.
  • Typical reverse is H1 TLS→H1 or H2→H1 (~1.1× YARP on Win/Linux tiny GET). With larger bodies, see Heavier reverse (@ 88187363).

How to read the tables

  • Bold RPS is sustain (last concurrency that still met error/latency SLOs). When peak differs, it appears in the same cell as <sub>(peak N · …)</sub> with RSS/CPU. When sustain equals peak, peak is omitted. Footnotes also include microseconds of CPU per request when CPU was sampled.
  • 🥇 = best among OS-possible peers on that row (highest sustain RPS; on a tie, lower memory then lower CPU%). On 64 KiB and 256 KiB rows the medal follows goodput (sustain RPS × request and response bytes). On tiny Reverse that is Titanium / nginx / YARP (plus HAProxy / Envoy on Linux/macOS). The same rule applies to heavier reverse, architecture-sensitive, TLS-cost, gRPC, and WebSocket peer tables. Gold on tiny GET is that frame-copy best case, not “Titanium is 1.7× on all reverse.” For larger-body H2→H2, see the heavier tables.
  • MITM is published on Linux only (Titanium-only, no peer medal). Lite÷Reverse / Full÷Reverse = Titanium MITM sustain ÷ Titanium reverse sustain on the same Client×Origin pair — the overhead of decrypting and intercepting versus bare reverse, not versus nginx or YARP.
  • Not possible = cannot do that path. Not measured = path exists but no published number yet. When every row would be Not possible for a peer, that column is omitted and a note above the table explains why.

Contents

Measurement environment

All three OS use public-repo GitHub-hosted runners: Windows / Linux at 4 vCPU / 16 GiB / 14 GB SSD, macOS at macos-15 Apple Silicon (M1) 3-core / 7 GB (pinned, not macos-latest). macOS is Apple Silicon because that is what Mac users run today; the runner sizes differ, which is fine because we compare products within an OS. Same harness knobs: warmup 2s / measure 8s; concurrency 8, 16, 32, 64; median of 3 repeats. Prefer Titanium÷YARP / Titanium÷nginx ratios over absolute RPS.

HTTP/2 and HTTP/3 open min(concurrency, max(4 × cores, 16)) client connections, one SocketsHttpHandler each. On the 4 vCPU runners that is 8, 16, 16, and 16 connections at c = 8, 16, 32, 64. HTTP/1 stays one connection per in-flight request. The generator is in-process .NET SocketsHttpHandler on the same machine as the origin and the proxy. Client, origin, and proxy share those cores, so the proxy's CPU share stays below a full box.

nginx TCP and TLS listen lines use reuseport. Linux and Windows keep worker_processes auto. macOS stays worker_processes 1. HAProxy nbthread and Envoy --concurrency stay at the processor count.

Large-body flow control is the same class on every peer: nginx http2_chunk_size 16k; HAProxy tune.bufsize 65536 and tune.h2.initial-window-size 65536; Envoy stream window 65535 bytes and connection window 1 MiB. Titanium's defaults stay a 65535-byte connection window and a 16384-byte max frame.

Cells for 64 KiB and 256 KiB bodies include goodput (MiB/s) and microseconds of CPU per request. Those rows are ranked on goodput. Smaller bodies stay ranked on RPS.

On the Linux H2 TLS→H1 plain row at c=64 (16 connections, @ 88187363), proxy CPU was about 43% for nginx, 45% for HAProxy, and 54% for Envoy. Windows nginx on the same H2 client shape stayed near 25% (one core). That measured shape is the one published here.

Laptop High-perf / cool-paired Windows numbers live on Performance Local Lab. Do not mix those absolutes into the tables below.

Windows (GitHub-hosted windows-latest)

OS Windows Server (GitHub-hosted windows-latest)
CPU 4 logical processors
RAM 16 GiB
Runtime .NET 10.0.x
nginx nginx/Windows 1.31.3 (same-OS only; no QUIC)
HAProxy Not possible on Windows (no official port)
Envoy Not possible on Windows (upstream discontinued Windows builds)
YARP Yarp.ReverseProxy 2.3.0
Harness RpsLoadProbe Release; median of 3 repeats

Linux (GitHub-hosted ubuntu-latest)

OS Ubuntu 24.04.x LTS
CPU 4 logical processors (AMD EPYC; runners this pass were 7763 / 9V74)
RAM 16 GiB
Runtime .NET 10.0.11
nginx nginx/1.31.4 (nginx.org mainline, --with-http_v3_module)
HAProxy HAProxy 3.2.23 built with USE_QUIC (GHA; Ubuntu distro 2.8 is not QUIC-capable)
Envoy pinned GitHub release static binary 1.36.7 (HTTP/3 compiled in)
YARP Yarp.ReverseProxy 2.3.0
Harness RpsLoadProbe Release; median of 3 repeats where noted

macOS (GitHub-hosted macos-15, Apple Silicon)

OS macOS 15 (GitHub-hosted macos-15, Apple Silicon arm64)
CPU 3 logical processors (Apple M1)
RAM 7 GB
Runtime .NET 10.0.x
nginx Homebrew nginx with --with-http_v3_module (workflow fails if missing)
HAProxy Homebrew haproxy with USE_QUIC (workflow fails if missing; 3.2.23 osx source fallback)
Envoy Homebrew bottle when present; else pinned darwin-arm64 1.36.7 (official GitHub assets are Linux-only). HTTP/3 compiled in.
MsQuic Homebrew libmsquic + openssl@3 on DYLD_LIBRARY_PATH / DYLD_FALLBACK_LIBRARY_PATH (QuicListener.IsSupported)
YARP Yarp.ReverseProxy 2.3.0
Harness RpsLoadProbe Release; median of 3 repeats where noted

Use the pinned macos-15 label (not macos-latest) so the image does not change between runs.

Saturation control

Calibration for the shared 4 vCPU loopback shape: how close client + origin are to saturated before ranking reverse peers. Tiny keep-alive GET. Median of 3 repeats @ 88187363 — 38550341872. Warmup 2s / measure 8s; concurrency 8, 16, 32, 64. Block A % of origin-HttpClient uses median peak RPS. Blocks B/C use peer÷YARP / ÷nginx on median peak (not % of H1 origin). RPS cells embed median RSS / CPU for the proxy child plus its full descendant tree (serve-proxy → nginx master → workers); origin-direct samples the origin child. Product matrices below use matched dotnet-httpclient only (not bombardier).

Block A — H1 plain

Arm Generator Notes
origin-direct dotnet-httpclient No proxy — origin ceiling under the same client used for publishable ratios
origin-direct-bombardier bombardier External client check (CI installs bombardier)
bare-reverse-http1 dotnet-httpclient Thin C# H1 reverse (BareHttp1ReverseProxy) — .NET runtime / loopback ceiling for a three-process reverse hop; not a product peer
nginx-reverse-http1 / yarp-reverse-http1 / twp-reverse-http1 dotnet-httpclient Product peers (medals among these three only)

Windows (windows-latest)

Arm Generator RPS % of origin-HttpClient
origin-direct dotnet-httpclient 63,518
(55 MiB / 44.2% CPU · 27.8 µs CPU/req)
100.0%
origin-direct-bombardier bombardier 48,572
(55 MiB / 24.6% CPU · 20.3 µs CPU/req)
76.5%
bare-reverse-http1 dotnet-httpclient 31,387
(56 MiB / 46.6% CPU · 59.4 µs CPU/req)
49.4%
nginx-reverse-http1 dotnet-httpclient 19,584
(126 MiB / 24.7% CPU · 50.5 µs CPU/req)
30.8%
yarp-reverse-http1 dotnet-httpclient 27,207
(86 MiB / 50.3% CPU · 73.9 µs CPU/req)
42.8%
twp-reverse-http1 dotnet-httpclient 🥇 31,719
(77 MiB / 49.5% CPU · 62.5 µs CPU/req)
49.9%

Linux (ubuntu-latest)

Arm Generator RPS % of origin-HttpClient
origin-direct dotnet-httpclient 105,520
(81 MiB / 43.2% CPU · 16.4 µs CPU/req)
100.0%
origin-direct-bombardier bombardier 57,947
(80 MiB / 31.6% CPU · 21.8 µs CPU/req)
54.9%
bare-reverse-http1 dotnet-httpclient 47,669
(73 MiB / 43.7% CPU · 36.7 µs CPU/req)
45.2%
nginx-reverse-http1 dotnet-httpclient 🥇 61,370
(76 MiB / 39.1% CPU · 25.5 µs CPU/req)
58.2%
yarp-reverse-http1 dotnet-httpclient 41,004
(118 MiB / 49.4% CPU · 48.2 µs CPU/req)
38.9%
twp-reverse-http1 dotnet-httpclient 46,843
(85 MiB / 49.0% CPU · 41.8 µs CPU/req)
44.4%

macOS (macos-15, Apple Silicon)

Arm Generator RPS % of origin-HttpClient
origin-direct dotnet-httpclient 59,657
(89 MiB / 23.9% CPU · 16.0 µs CPU/req)
100.0%
origin-direct-bombardier bombardier 54,519
(89 MiB / 23.7% CPU · 17.4 µs CPU/req)
91.4%
bare-reverse-http1 dotnet-httpclient 20,943
(91 MiB / 19.3% CPU · 36.8 µs CPU/req)
35.1%
nginx-reverse-http1 dotnet-httpclient 29,506
(68 MiB / 19.8% CPU · 26.9 µs CPU/req)
49.5%
yarp-reverse-http1 dotnet-httpclient 23,030
(148 MiB / 33.0% CPU · 57.4 µs CPU/req)
38.6%
twp-reverse-http1 dotnet-httpclient 🥇 34,852
(114 MiB / 36.2% CPU · 41.5 µs CPU/req)
58.4%

On this macOS run origin-direct was 23.9% CPU, so the % of origin-HttpClient column is not a ceiling when that CPU is near idle. Rank those Mac peers by RPS.

Reverse peers on Block A @ 88187363: Windows TWP is 49.9% of origin-direct and Linux TWP is 44.4%. Prefer the % column over absolute RPS across runs. Bare and origin-direct are controls (not medal peers).

Block B — H2 TLS→H1

Peer ratios (÷YARP / ÷nginx) on median peak; RPS cells embed (MiB / CPU%).

Windows (windows-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http2 dotnet-httpclient 11,105
(142 MiB / 24.8% CPU · 89.2 µs CPU/req)
0.33× 1.00×
yarp-reverse-http2 dotnet-httpclient 33,327
(107 MiB / 51.8% CPU · 62.1 µs CPU/req)
1.00× 3.00×
twp-reverse-http2-cleartext dotnet-httpclient 🥇 36,554
(122 MiB / 53.1% CPU · 58.1 µs CPU/req)
1.10× 3.29×

Linux (ubuntu-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http2 dotnet-httpclient 38,560
(101 MiB / 40.6% CPU · 42.1 µs CPU/req)
1.02× 1.00×
yarp-reverse-http2 dotnet-httpclient 37,890
(133 MiB / 48.5% CPU · 51.2 µs CPU/req)
1.00× 0.98×
twp-reverse-http2-cleartext dotnet-httpclient 🥇 41,224
(153 MiB / 49.4% CPU · 47.9 µs CPU/req)
1.09× 1.07×

macOS (macos-15, Apple Silicon)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http2 dotnet-httpclient 34,494
(83 MiB / 25.3% CPU · 29.3 µs CPU/req)
0.90× 1.00×
yarp-reverse-http2 dotnet-httpclient 🥇 38,169
(158 MiB / 42.2% CPU · 44.2 µs CPU/req)
1.00× 1.11×
twp-reverse-http2-cleartext dotnet-httpclient 37,118
(175 MiB / 41.4% CPU · 44.6 µs CPU/req)
0.97× 1.08×

Block C — H3→H1

Same layout as Block B. Requires QuicListener. nginx needs http_v3_module (Windows nginx has no QUIC). HAProxy needs USE_QUIC (GHA Linux/macOS require it). Envoy 1.20+ includes HTTP/3.

Medals are nginx / YARP / Titanium only, from the same run as Blocks A and B (38550341872). On the Linux run HAProxy was 23,149 (88 MiB / 35.3% CPU) and Envoy 8,803 (132 MiB / 58.8% CPU). On the macOS run HAProxy was 20,965 (105 MiB / 29.1% CPU) and Envoy 9,246 (126 MiB / 56.5% CPU).

Windows (windows-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient Not possible (no QUIC) — —
yarp-reverse-http3-cleartext dotnet-httpclient 9,918
(157 MiB / 49.2% CPU · 198.4 µs CPU/req)
1.00× —
twp-reverse-http3-cleartext dotnet-httpclient 🥇 10,963
(113 MiB / 48.6% CPU · 177.3 µs CPU/req)
1.11× —

Linux (ubuntu-latest)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient 22,691
(106 MiB / 28.1% CPU · 49.5 µs CPU/req)
0.92× 1.00×
yarp-reverse-http3-cleartext dotnet-httpclient 19,605
(198 MiB / 50.0% CPU · 102.0 µs CPU/req)
1.00× 1.09×
twp-reverse-http3-cleartext dotnet-httpclient 🥇 23,355
(156 MiB / 50.8% CPU · 86.9 µs CPU/req)
1.19× 1.29×

macOS (macos-15, Apple Silicon)

Arm Generator RPS ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient 11,572
(peak 20,033 · 85 MiB / 22.4% CPU · 77.4 µs CPU/req)
1.38× 1.00×
yarp-reverse-http3-cleartext dotnet-httpclient 14,527
(218 MiB / 43.8% CPU · 120.6 µs CPU/req)
1.00× 0.73×
twp-reverse-http3-cleartext dotnet-httpclient 🥇 16,362
(140 MiB / 44.7% CPU · 109.2 µs CPU/req)
1.13× 0.82×

Windows — Titanium vs nginx vs HAProxy vs Envoy vs YARP

Client / origin: HTTP version and whether TLS is used (plain = cleartext, TLS = encrypted, QUIC = HTTP/3).

Reverse

Median of 3 repeats on windows-latest (4 vCPU / 16 GiB). Bare reverse 5×5 @ 88187363 — compare-product 38550284913. Warmup 2s / measure 8s; concurrency 8, 16, 32, 64. Prefer TWP÷peer ratios over absolute RPS. RPS cells include median RSS / CPU at the peak-RPS step as <br><sub>(MiB / CPU%)</sub>. nginx terminate peers use keepalive 256 + streaming buffers. HAProxy / Envoy are Linux-only peers (no official Windows port). Laptop High-perf / cool-paired numbers stay on the local lab. Product 5×5 is ~56-byte JSON keep-alive GET; H2/H3 same-protocol cells are mostly header work with a tiny body (Titanium best case) — see Why this comparison is fair.

Load generators: Reverse inbound H3 arms use dotnet-httpclient (http_version=3.0, RequestVersionExact). nginx/Windows is same-OS only (no QUIC). HAProxy/Envoy are Linux-only terminate peers.

Client Origin TWP nginx YARP
HTTP/1 · plain HTTP/1 · plain 🥇 37,430
(81 MiB / 47.8% CPU · 51.1 µs CPU/req)
29,433
(126 MiB / 24.8% CPU · 33.8 µs CPU/req)
32,904
(89 MiB / 47.9% CPU · 58.3 µs CPU/req)
HTTP/1 · plain HTTP/1 · TLS 🥇 34,096
(90 MiB / 50.9% CPU · 59.7 µs CPU/req)
15,589
(137 MiB / 24.9% CPU · 63.8 µs CPU/req)
30,982
(101 MiB / 49.1% CPU · 63.4 µs CPU/req)
HTTP/1 · plain HTTP/2 · plain 🥇 36,976
(103 MiB / 46.7% CPU · 50.6 µs CPU/req)
Not possible (no H2 upstream) 32,787
(90 MiB / 48% CPU · 58.6 µs CPU/req)
HTTP/1 · plain HTTP/2 · TLS 🥇 40,192
(121 MiB / 48.5% CPU · 48.3 µs CPU/req)
Not possible (no H2 upstream) 36,754
(100 MiB / 48% CPU · 52.3 µs CPU/req)
HTTP/1 · plain HTTP/3 · QUIC 🥇 18,479
(111 MiB / 49.5% CPU · 107.2 µs CPU/req)
Not possible (no H3 upstream) 18,102
(119 MiB / 49.8% CPU · 109.9 µs CPU/req)
HTTP/1 · TLS HTTP/1 · plain 🥇 21,672
(88 MiB / 46.8% CPU · 86.4 µs CPU/req)
9,372
(142 MiB / 24.1% CPU · 102.9 µs CPU/req)
19,328
(106 MiB / 45.4% CPU · 93.9 µs CPU/req)
HTTP/1 · TLS HTTP/1 · TLS 🥇 21,235
(90 MiB / 48.2% CPU · 90.9 µs CPU/req)
8,376
(144 MiB / 24.7% CPU · 118.0 µs CPU/req)
19,324
(105 MiB / 48.3% CPU · 100.0 µs CPU/req)
HTTP/1 · TLS HTTP/2 · plain 🥇 28,535
(110 MiB / 43.6% CPU · 61.0 µs CPU/req)
Not possible (no H2 upstream) 26,359
(110 MiB / 45.2% CPU · 68.6 µs CPU/req)
HTTP/1 · TLS HTTP/2 · TLS 🥇 25,344
(117 MiB / 47.3% CPU · 74.7 µs CPU/req)
Not possible (no H2 upstream) 24,418
(107 MiB / 47.2% CPU · 77.4 µs CPU/req)
HTTP/1 · TLS HTTP/3 · QUIC 🥇 19,344
(111 MiB / 50.7% CPU · 104.9 µs CPU/req)
Not possible (no H3 upstream) 17,188
(128 MiB / 48.3% CPU · 112.5 µs CPU/req)
HTTP/2 · plain HTTP/1 · plain 🥇 48,595
(120 MiB / 47.3% CPU · 38.9 µs CPU/req)
16,428
(128 MiB / 24.7% CPU · 60.1 µs CPU/req)
47,460
(94 MiB / 55.7% CPU · 46.9 µs CPU/req)
HTTP/2 · plain HTTP/1 · TLS 🥇 28,068
(119 MiB / 52.4% CPU · 74.7 µs CPU/req)
6,423
(138 MiB / 24.7% CPU · 153.9 µs CPU/req)
25,852
(105 MiB / 54.3% CPU · 84.0 µs CPU/req)
HTTP/2 · plain HTTP/2 · plain 🥇 71,440
(100 MiB / 34.6% CPU · 19.4 µs CPU/req)
Not possible (no H2 upstream) 52,060
(97 MiB / 50.8% CPU · 39.0 µs CPU/req)
HTTP/2 · plain HTTP/2 · TLS 🥇 86,057
(116 MiB / 36.1% CPU · 16.8 µs CPU/req)
Not possible (no H2 upstream) 63,619
(109 MiB / 49.1% CPU · 30.9 µs CPU/req)
HTTP/2 · plain HTTP/3 · QUIC 🥇 27,800
(138 MiB / 52.2% CPU · 75.1 µs CPU/req)
Not possible (no H3 upstream) 26,134
(124 MiB / 53.9% CPU · 82.5 µs CPU/req)
HTTP/2 · TLS HTTP/1 · plain 🥇 31,026
(126 MiB / 51.9% CPU · 66.9 µs CPU/req)
7,772
(142 MiB / 24.7% CPU · 127.1 µs CPU/req)
27,808
(105 MiB / 50.8% CPU · 73.0 µs CPU/req)
HTTP/2 · TLS HTTP/1 · TLS 🥇 37,212
(115 MiB / 49.5% CPU · 53.3 µs CPU/req)
12,357
(145 MiB / 24.9% CPU · 80.6 µs CPU/req)
34,602
(105 MiB / 51.2% CPU · 59.2 µs CPU/req)
HTTP/2 · TLS HTTP/2 · plain 🥇 82,994
(119 MiB / 36.7% CPU · 17.7 µs CPU/req)
Not possible (no H2 upstream) 59,748
(108 MiB / 52.6% CPU · 35.2 µs CPU/req)
HTTP/2 · TLS HTTP/2 · TLS 🥇 53,100
(112 MiB / 35.7% CPU · 26.9 µs CPU/req)
Not possible (no H2 upstream) 40,755
(107 MiB / 47.7% CPU · 46.8 µs CPU/req)
HTTP/2 · TLS HTTP/3 · QUIC 🥇 22,934
(155 MiB / 51.5% CPU · 89.8 µs CPU/req)
Not possible (no H3 upstream) 21,347
(127 MiB / 53.1% CPU · 99.5 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · plain 🥇 9,080
(113 MiB / 46.7% CPU · 205.9 µs CPU/req)
Not possible (no QUIC) 6,455
(152 MiB / 43.6% CPU · 270.2 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 8,412
(114 MiB / 50.3% CPU · 239.3 µs CPU/req)
Not possible (no QUIC) 7,690
(161 MiB / 50% CPU · 260.0 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · plain 🥇 50,871
(155 MiB / 46.9% CPU · 36.9 µs CPU/req)
Not possible (no H2 upstream) 45,980
(176 MiB / 48.9% CPU · 42.5 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 12,058
(121 MiB / 48.1% CPU · 159.6 µs CPU/req)
Not possible (no H2 upstream) 10,490
(156 MiB / 49.5% CPU · 188.6 µs CPU/req)
HTTP/3 · QUIC HTTP/3 · QUIC 🥇 13,576
(119 MiB / 46.6% CPU · 137.3 µs CPU/req)
Not possible (no H3 upstream) 11,023
(171 MiB / 49.2% CPU · 178.6 µs CPU/req)

Linux — Titanium vs nginx vs HAProxy vs Envoy vs YARP

Reverse

Median of 3 repeats on ubuntu-latest (4 vCPU / 16 GiB). Bare reverse 5×5 @ 88187363 — compare-product 38550284913. Warmup 2s / measure 8s; concurrency 8, 16, 32, 64. Linux nginx is the authoritative nginx baseline. HAProxy (3.2 USE_QUIC) and Envoy (GitHub release, HTTP/3 compiled in) run on the same loopback shape as nginx/YARP. nginx terminate peers use keepalive 256 + streaming buffers. The RPS workflow installs nginx.org mainline (http_v3_module), a QUIC-enabled HAProxy, Envoy, and libmsquic. Prefer ratios over absolute RPS. Product 5×5 is ~56-byte JSON keep-alive GET; H2/H3 same-protocol cells are mostly header work with a tiny body (Titanium best case) — see Why this comparison is fair.

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · plain HTTP/1 · plain 62,492
(83 MiB / 49.3% CPU · 31.5 µs CPU/req)
🥇 75,583
(76 MiB / 40.2% CPU · 21.3 µs CPU/req)
64,687
(66 MiB / 42.7% CPU · 26.4 µs CPU/req)
48,875
(115 MiB / 52.9% CPU · 43.3 µs CPU/req)
53,812
(116 MiB / 47% CPU · 35.0 µs CPU/req)
HTTP/1 · plain HTTP/1 · TLS 36,279
(104 MiB / 48.2% CPU · 53.1 µs CPU/req)
🥇 44,496
(93 MiB / 40.6% CPU · 36.5 µs CPU/req)
40,003
(71 MiB / 44.3% CPU · 44.3 µs CPU/req)
28,475
(118 MiB / 56.4% CPU · 79.3 µs CPU/req)
33,325
(128 MiB / 49.2% CPU · 59.1 µs CPU/req)
HTTP/1 · plain HTTP/2 · plain 🥇 40,445
(127 MiB / 51.2% CPU · 50.6 µs CPU/req)
Not possible (no H2 upstream) 25,662
(66 MiB / 43.8% CPU · 68.2 µs CPU/req)
21,416
(116 MiB / 63.1% CPU · 117.8 µs CPU/req)
35,594
(125 MiB / 49% CPU · 55.1 µs CPU/req)
HTTP/1 · plain HTTP/2 · TLS 🥇 33,028
(149 MiB / 48.9% CPU · 59.2 µs CPU/req)
Not possible (no H2 upstream) 26,523
(67 MiB / 46.7% CPU · 70.5 µs CPU/req)
20,559
(117 MiB / 61.6% CPU · 119.9 µs CPU/req)
30,034
(135 MiB / 47.4% CPU · 63.2 µs CPU/req)
HTTP/1 · plain HTTP/3 · QUIC 🥇 23,130
(135 MiB / 54.3% CPU · 94.0 µs CPU/req)
Not possible (no H3 upstream) Not possible (no H3 upstream) 10,608
(121 MiB / 56.9% CPU · 214.4 µs CPU/req)
21,364
(155 MiB / 49% CPU · 91.7 µs CPU/req)
HTTP/1 · TLS HTTP/1 · plain 23,622
(105 MiB / 49.7% CPU · 84.2 µs CPU/req)
🥇 28,279
(99 MiB / 41.9% CPU · 59.2 µs CPU/req)
26,673
(82 MiB / 44.4% CPU · 66.5 µs CPU/req)
16,744
(127 MiB / 58.6% CPU · 139.9 µs CPU/req)
20,318
(132 MiB / 50.8% CPU · 99.9 µs CPU/req)
HTTP/1 · TLS HTTP/1 · TLS 34,580
(109 MiB / 46.2% CPU · 53.5 µs CPU/req)
🥇 43,561
(104 MiB / 38.6% CPU · 35.5 µs CPU/req)
40,702
(85 MiB / 42% CPU · 41.3 µs CPU/req)
29,743
(127 MiB / 53.6% CPU · 72.2 µs CPU/req)
30,648
(142 MiB / 48% CPU · 62.6 µs CPU/req)
HTTP/1 · TLS HTTP/2 · plain 🥇 35,383
(139 MiB / 50.8% CPU · 57.5 µs CPU/req)
Not possible (no H2 upstream) 23,801
(83 MiB / 43.3% CPU · 72.8 µs CPU/req)
23,065
(127 MiB / 58.3% CPU · 101.1 µs CPU/req)
30,801
(144 MiB / 49.9% CPU · 64.8 µs CPU/req)
HTTP/1 · TLS HTTP/2 · TLS 🥇 55,982
(160 MiB / 47.2% CPU · 33.7 µs CPU/req)
Not possible (no H2 upstream) 50,034
(83 MiB / 45.5% CPU · 36.4 µs CPU/req)
47,541
(126 MiB / 51.6% CPU · 43.4 µs CPU/req)
52,007
(149 MiB / 46.6% CPU · 35.8 µs CPU/req)
HTTP/1 · TLS HTTP/3 · QUIC 🥇 28,251
(157 MiB / 53.2% CPU · 75.3 µs CPU/req)
Not possible (no H3 upstream) Not possible (no H3 upstream) 14,076
(131 MiB / 54.2% CPU · 153.9 µs CPU/req)
25,130
(169 MiB / 49.5% CPU · 78.8 µs CPU/req)
HTTP/2 · plain HTTP/1 · plain 🥇 66,631
(147 MiB / 51.5% CPU · 30.9 µs CPU/req)
51,209
(78 MiB / 45.1% CPU · 35.2 µs CPU/req)
64,393
(64 MiB / 43.9% CPU · 27.3 µs CPU/req)
52,573
(116 MiB / 53.9% CPU · 41.0 µs CPU/req)
64,640
(125 MiB / 48% CPU · 29.7 µs CPU/req)
HTTP/2 · plain HTTP/1 · TLS 🥇 27,473
(147 MiB / 51.1% CPU · 74.3 µs CPU/req)
22,255
(95 MiB / 45.8% CPU · 82.3 µs CPU/req)
27,447
(72 MiB / 45% CPU · 65.6 µs CPU/req)
18,255
(119 MiB / 59.7% CPU · 130.9 µs CPU/req)
24,655
(133 MiB / 51.6% CPU · 83.7 µs CPU/req)
HTTP/2 · plain HTTP/2 · plain 🥇 97,777
(141 MiB / 41.4% CPU · 16.9 µs CPU/req)
Not possible (no H2 upstream) 52,788
(66 MiB / 44.1% CPU · 33.4 µs CPU/req)
40,830
(116 MiB / 58.5% CPU · 57.3 µs CPU/req)
64,454
(132 MiB / 47.6% CPU · 29.5 µs CPU/req)
HTTP/2 · plain HTTP/2 · TLS 🥇 50,675
(161 MiB / 40.6% CPU · 32.0 µs CPU/req)
Not possible (no H2 upstream) 27,240
(68 MiB / 46% CPU · 67.5 µs CPU/req)
21,941
(117 MiB / 59.6% CPU · 108.6 µs CPU/req)
35,612
(138 MiB / 47.3% CPU · 53.1 µs CPU/req)
HTTP/2 · plain HTTP/3 · QUIC 🥇 27,796
(175 MiB / 51.3% CPU · 73.8 µs CPU/req)
Not possible (no H3 upstream) Not possible (no H3 upstream) 10,914
(119 MiB / 57.5% CPU · 210.7 µs CPU/req)
24,618
(157 MiB / 48.1% CPU · 78.2 µs CPU/req)
HTTP/2 · TLS HTTP/1 · plain 🥇 63,488
(151 MiB / 51.1% CPU · 32.2 µs CPU/req)
52,870
(102 MiB / 42.5% CPU · 32.2 µs CPU/req)
61,105
(81 MiB / 44.2% CPU · 29.0 µs CPU/req)
51,574
(128 MiB / 53.2% CPU · 41.2 µs CPU/req)
59,373
(135 MiB / 48.2% CPU · 32.5 µs CPU/req)
HTTP/2 · TLS HTTP/1 · TLS 🥇 37,822
(149 MiB / 49.6% CPU · 52.4 µs CPU/req)
32,386
(107 MiB / 42.3% CPU · 52.2 µs CPU/req)
36,644
(85 MiB / 44.1% CPU · 48.2 µs CPU/req)
28,175
(127 MiB / 55.6% CPU · 78.9 µs CPU/req)
34,444
(135 MiB / 49% CPU · 56.9 µs CPU/req)
HTTP/2 · TLS HTTP/2 · plain 🥇 56,696
(163 MiB / 41.7% CPU · 29.4 µs CPU/req)
Not possible (no H2 upstream) 27,590
(83 MiB / 45.8% CPU · 66.4 µs CPU/req)
22,395
(126 MiB / 61% CPU · 108.9 µs CPU/req)
36,819
(139 MiB / 48.7% CPU · 52.9 µs CPU/req)
HTTP/2 · TLS HTTP/2 · TLS 🥇 68,127
(166 MiB / 40.9% CPU · 24.0 µs CPU/req)
Not possible (no H2 upstream) 40,761
(83 MiB / 44.7% CPU · 43.8 µs CPU/req)
35,700
(125 MiB / 56.3% CPU · 63.1 µs CPU/req)
47,554
(140 MiB / 46.6% CPU · 39.2 µs CPU/req)
HTTP/2 · TLS HTTP/3 · QUIC 🥇 35,713
(195 MiB / 52.1% CPU · 58.4 µs CPU/req)
Not possible (no H3 upstream) Not possible (no H3 upstream) 15,252
(127 MiB / 54.1% CPU · 141.8 µs CPU/req)
31,004
(170 MiB / 47.6% CPU · 61.3 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · plain 22,420
(153 MiB / 51% CPU · 91.1 µs CPU/req)
28,315
(107 MiB / 27.5% CPU · 38.8 µs CPU/req)
🥇 30,241
(87 MiB / 33.9% CPU · 44.9 µs CPU/req)
11,510
(133 MiB / 55.7% CPU · 193.6 µs CPU/req)
17,810
(199 MiB / 50.6% CPU · 113.7 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · TLS 36,115
(186 MiB / 52.5% CPU · 58.1 µs CPU/req)
0
(peak 23,614 · 115 MiB / 32.6% CPU)
🥇 43,349
(88 MiB / 34.6% CPU · 31.9 µs CPU/req)
20,573
(133 MiB / 48.4% CPU · 94.0 µs CPU/req)
29,449
(215 MiB / 48.8% CPU · 66.3 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · plain 22,203
(151 MiB / 54.1% CPU · 97.5 µs CPU/req)
Not possible (no H2 upstream) 🥇 22,310
(87 MiB / 35.4% CPU · 63.5 µs CPU/req)
9,831
(132 MiB / 58.8% CPU · 239.1 µs CPU/req)
18,350
(202 MiB / 49.6% CPU · 108.2 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · TLS 21,458
(158 MiB / 52.9% CPU · 98.7 µs CPU/req)
Not possible (no H2 upstream) 🥇 23,532
(88 MiB / 35.3% CPU · 60.0 µs CPU/req)
11,409
(133 MiB / 56% CPU · 196.2 µs CPU/req)
18,753
(208 MiB / 49.2% CPU · 105.0 µs CPU/req)
HTTP/3 · QUIC HTTP/3 · QUIC 🥇 16,206
(155 MiB / 50.4% CPU · 124.5 µs CPU/req)
Not possible (no H3 upstream) Not possible (no H3 upstream) 6,252
(131 MiB / 56% CPU · 357.9 µs CPU/req)
13,221
(206 MiB / 48.5% CPU · 146.8 µs CPU/req)

MITM (TWP only)

Same Client×Origin wires with interception on (compare-product 38550284913). Lite = no-op handlers (unchanged-lite finish). Full = append-only header mutation (harness: one probe header each way; product: generic append-only relay via MitmCompressedRelayHelper). nginx/HAProxy/Envoy/YARP cannot MITM. Lite÷Reverse / Full÷Reverse vs bare reverse (same job / comparison-group shard). Completion gate: Lite ≥ 0.25× and Full ≥ 0.25× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP ÷ closest peer ≥ 0.40×.

v1 append-only relay (2026-08-27): Pre-fix H2→H2 Full÷Reverse was 0.13–0.16× (32960766249). Post-fix @ df172718: H2 plain→H2 plain Full 0.77–0.79×, H3→H1 Full 0.91–0.93×, all MITM arms ≥ 0.70× on median of 33041445371, 33055267086, 33055272140.

v2 drop-only + non-unique append (2026-08-27): MitmStaticRebuildHelper rebuilds static HPACK/QPACK after 1–4 unique header drops; trailing non-unique appends stay on compressed relay. @ 41f4adee: all MITM arms ≥ 0.70× on GHA median (33087088466, 33087091622, 33105885748 Linux; 33087085235, 33087088466, 33087091622 Windows). H2 plain→H2 plain Full 0.77–0.79× (Win) / 0.78× (Lin).

Client Origin Lite sustain Full sustain Lite÷Reverse Full÷Reverse
HTTP/1 · plain HTTP/1 · plain 60,365
(90 MiB / 49.5% CPU · 32.8 µs CPU/req)
57,485
(93 MiB / 48.3% CPU · 33.6 µs CPU/req)
0.97× 0.92×
HTTP/1 · plain HTTP/1 · TLS 36,812
(108 MiB / 49.2% CPU · 53.5 µs CPU/req)
36,058
(111 MiB / 49.1% CPU · 54.5 µs CPU/req)
1.01× 0.99×
HTTP/1 · plain HTTP/2 · plain 39,591
(118 MiB / 51.8% CPU · 52.3 µs CPU/req)
38,439
(127 MiB / 52.5% CPU · 54.7 µs CPU/req)
0.98× 0.95×
HTTP/1 · plain HTTP/2 · TLS 32,228
(151 MiB / 49.7% CPU · 61.6 µs CPU/req)
31,780
(148 MiB / 49.4% CPU · 62.1 µs CPU/req)
0.98× 0.96×
HTTP/1 · plain HTTP/3 · QUIC 22,629
(138 MiB / 53.6% CPU · 94.7 µs CPU/req)
22,446
(142 MiB / 54.4% CPU · 96.9 µs CPU/req)
0.98× 0.97×
HTTP/1 · TLS HTTP/1 · plain 23,462
(109 MiB / 50.4% CPU · 85.9 µs CPU/req)
22,737
(116 MiB / 50.4% CPU · 88.6 µs CPU/req)
0.99× 0.96×
HTTP/1 · TLS HTTP/1 · TLS 34,291
(117 MiB / 46.3% CPU · 54.0 µs CPU/req)
33,368
(116 MiB / 46.3% CPU · 55.5 µs CPU/req)
0.99× 0.96×
HTTP/1 · TLS HTTP/2 · plain 34,126
(139 MiB / 51.9% CPU · 60.8 µs CPU/req)
34,064
(145 MiB / 52.6% CPU · 61.8 µs CPU/req)
0.96× 0.96×
HTTP/1 · TLS HTTP/2 · TLS 55,217
(159 MiB / 48.4% CPU · 35.1 µs CPU/req)
54,393
(161 MiB / 49.1% CPU · 36.1 µs CPU/req)
0.99× 0.97×
HTTP/1 · TLS HTTP/3 · QUIC 28,264
(168 MiB / 54.2% CPU · 76.7 µs CPU/req)
27,297
(168 MiB / 53.5% CPU · 78.4 µs CPU/req)
1× 0.97×
HTTP/2 · plain HTTP/1 · plain 67,101
(143 MiB / 52.7% CPU · 31.4 µs CPU/req)
65,903
(147 MiB / 51.7% CPU · 31.4 µs CPU/req)
1.01× 0.99×
HTTP/2 · plain HTTP/1 · TLS 27,378
(149 MiB / 52.7% CPU · 77.0 µs CPU/req)
26,341
(160 MiB / 52.5% CPU · 79.8 µs CPU/req)
1× 0.96×
HTTP/2 · plain HTTP/2 · plain 81,093
(140 MiB / 44.6% CPU · 22.0 µs CPU/req)
77,506
(127 MiB / 44.6% CPU · 23.0 µs CPU/req)
0.83× 0.79×
HTTP/2 · plain HTTP/2 · TLS 43,380
(160 MiB / 43.7% CPU · 40.3 µs CPU/req)
43,569
(158 MiB / 44.1% CPU · 40.5 µs CPU/req)
0.86× 0.86×
HTTP/2 · plain HTTP/3 · QUIC 27,890
(169 MiB / 51.6% CPU · 74.1 µs CPU/req)
27,438
(174 MiB / 52.2% CPU · 76.1 µs CPU/req)
1× 0.99×
HTTP/2 · TLS HTTP/1 · plain 61,941
(156 MiB / 51.8% CPU · 33.4 µs CPU/req)
60,314
(158 MiB / 51.2% CPU · 33.9 µs CPU/req)
0.98× 0.95×
HTTP/2 · TLS HTTP/1 · TLS 36,981
(165 MiB / 50.5% CPU · 54.7 µs CPU/req)
36,086
(167 MiB / 49.9% CPU · 55.3 µs CPU/req)
0.98× 0.95×
HTTP/2 · TLS HTTP/2 · plain 49,336
(160 MiB / 46% CPU · 37.3 µs CPU/req)
47,521
(159 MiB / 45.7% CPU · 38.5 µs CPU/req)
0.87× 0.84×
HTTP/2 · TLS HTTP/2 · TLS 59,698
(174 MiB / 43.6% CPU · 29.2 µs CPU/req)
58,520
(170 MiB / 44.3% CPU · 30.3 µs CPU/req)
0.88× 0.86×
HTTP/2 · TLS HTTP/3 · QUIC 35,010
(206 MiB / 51.6% CPU · 58.9 µs CPU/req)
35,001
(204 MiB / 51.6% CPU · 59.0 µs CPU/req)
0.98× 0.98×
HTTP/3 · QUIC HTTP/1 · plain 21,703
(157 MiB / 52.3% CPU · 96.4 µs CPU/req)
21,592
(152 MiB / 52.4% CPU · 97.0 µs CPU/req)
0.97× 0.96×
HTTP/3 · QUIC HTTP/1 · TLS 32,339
(186 MiB / 52.6% CPU · 65.0 µs CPU/req)
32,448
(181 MiB / 54.1% CPU · 66.7 µs CPU/req)
0.9× 0.9×
HTTP/3 · QUIC HTTP/2 · plain 19,964
(156 MiB / 54.3% CPU · 108.7 µs CPU/req)
19,988
(154 MiB / 54.2% CPU · 108.5 µs CPU/req)
0.9× 0.9×
HTTP/3 · QUIC HTTP/2 · TLS 21,956
(157 MiB / 53.8% CPU · 97.9 µs CPU/req)
21,753
(158 MiB / 53.7% CPU · 98.7 µs CPU/req)
1.02× 1.01×
HTTP/3 · QUIC HTTP/3 · QUIC 15,942
(157 MiB / 52.2% CPU · 131.0 µs CPU/req)
15,894
(156 MiB / 52.6% CPU · 132.5 µs CPU/req)
0.98× 0.98×

macOS — Titanium vs nginx vs HAProxy vs Envoy vs YARP

Reverse

Median of 3 repeats on macos-15 (Apple Silicon M1, 3-core / 7 GB). Bare reverse 5×5 @ 88187363 — compare-product 38550284913. Warmup 2s / measure 8s; concurrency 8, 16, 32, 64. Prefer TWP÷peer ratios over absolute RPS. RPS cells include median RSS / CPU at the peak-RPS step as <br><sub>(MiB / CPU%)</sub>. The RPS workflow installs Homebrew nginx (http_v3_module), Homebrew HAProxy with USE_QUIC (3.2 source fallback), Envoy (Homebrew bottle or pinned darwin-arm64 1.36.7), Homebrew libmsquic (+ DYLD_*), and YARP. Use the pinned macos-15 label, not macos-latest. Product 5×5 is ~56-byte JSON keep-alive GET; H2/H3 same-protocol cells are mostly header work with a tiny body (Titanium best case) — see Why this comparison is fair.

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · plain HTTP/1 · plain 6,242
(114 MiB / 10.4% CPU · 50.2 µs CPU/req)
15,858
(68 MiB / 13.3% CPU · 25.2 µs CPU/req)
🥇 20,000
(89 MiB / 20.4% CPU · 30.6 µs CPU/req)
10,654
(112 MiB / 28.2% CPU · 79.5 µs CPU/req)
7,365
(180 MiB / 11.6% CPU · 47.1 µs CPU/req)
HTTP/1 · plain HTTP/1 · TLS 9,894
(130 MiB / 17.3% CPU · 52.4 µs CPU/req)
9,745
(79 MiB / 12.9% CPU · 39.8 µs CPU/req)
6,932
(95 MiB / 10.4% CPU · 44.8 µs CPU/req)
🥇 12,908
(113 MiB / 27% CPU · 62.7 µs CPU/req)
4,625
(197 MiB / 9.4% CPU · 61.0 µs CPU/req)
HTTP/1 · plain HTTP/2 · plain 10,580
(122 MiB / 11.3% CPU · 31.9 µs CPU/req)
Not possible (no H2 upstream) 7,369
(95 MiB / 10.6% CPU · 43.2 µs CPU/req)
🥇 18,977
(111 MiB / 36.4% CPU · 57.6 µs CPU/req)
8,702
(214 MiB / 11.1% CPU · 38.3 µs CPU/req)
HTTP/1 · plain HTTP/2 · TLS 11,027
(168 MiB / 18.4% CPU · 50.0 µs CPU/req)
Not possible (no H2 upstream) 🥇 12,981
(98 MiB / 21.9% CPU · 50.6 µs CPU/req)
5,359
(112 MiB / 14.7% CPU · 82.2 µs CPU/req)
7,334
(178 MiB / 9.5% CPU · 38.7 µs CPU/req)
HTTP/1 · plain HTTP/3 · QUIC 4,000
(132 MiB / 11.6% CPU · 86.9 µs CPU/req)
Not possible (no H3 upstream) Not possible (no H3 upstream) 4,062
(116 MiB / 15.9% CPU · 117.1 µs CPU/req)
🥇 7,923
(165 MiB / 14.6% CPU · 55.4 µs CPU/req)
HTTP/1 · TLS HTTP/1 · plain 19,096
(130 MiB / 26.2% CPU · 41.2 µs CPU/req)
12,818
(85 MiB / 16.2% CPU · 38.0 µs CPU/req)
🥇 26,805
(112 MiB / 24.8% CPU · 27.7 µs CPU/req)
7,271
(121 MiB / 16.9% CPU · 69.8 µs CPU/req)
10,073
(156 MiB / 13.5% CPU · 40.3 µs CPU/req)
HTTP/1 · TLS HTTP/1 · TLS 4,345
(158 MiB / 16.9% CPU · 116.6 µs CPU/req)
5,987
(89 MiB / 12% CPU · 60.3 µs CPU/req)
🥇 14,239
(110 MiB / 23.6% CPU · 49.6 µs CPU/req)
3,264
(peak 4,417 · 123 MiB / 23.4% CPU · 214.8 µs CPU/req)
3,300
(peak 3,413 · 199 MiB / 15.2% CPU · 138.6 µs CPU/req)
HTTP/1 · TLS HTTP/2 · plain 🥇 11,140
(173 MiB / 19.5% CPU · 52.5 µs CPU/req)
Not possible (no H2 upstream) 6,351
(111 MiB / 13.5% CPU · 63.9 µs CPU/req)
5,762
(121 MiB / 18% CPU · 93.5 µs CPU/req)
5,649
(237 MiB / 8.5% CPU · 45.2 µs CPU/req)
HTTP/1 · TLS HTTP/2 · TLS 6,537
(206 MiB / 12.7% CPU · 58.5 µs CPU/req)
Not possible (no H2 upstream) 🥇 11,999
(111 MiB / 20.5% CPU · 51.3 µs CPU/req)
9,048
(121 MiB / 28.1% CPU · 93.2 µs CPU/req)
9,667
(204 MiB / 14.5% CPU · 45.0 µs CPU/req)
HTTP/1 · TLS HTTP/3 · QUIC 🥇 8,874
(166 MiB / 23.5% CPU · 79.4 µs CPU/req)
Not possible (no H3 upstream) Not possible (no H3 upstream) 6,706
(126 MiB / 33.6% CPU · 150.3 µs CPU/req)
3,378
(172 MiB / 10.8% CPU · 95.5 µs CPU/req)
HTTP/2 · plain HTTP/1 · plain 🥇 16,038
(220 MiB / 20% CPU · 37.3 µs CPU/req)
6,548
(70 MiB / 7.4% CPU · 34.0 µs CPU/req)
14,749
(96 MiB / 15.6% CPU · 31.7 µs CPU/req)
12,293
(111 MiB / 33.8% CPU · 82.4 µs CPU/req)
5,158
(182 MiB / 8.5% CPU · 49.7 µs CPU/req)
HTTP/2 · plain HTTP/1 · TLS 6,776
(242 MiB / 14% CPU · 62.0 µs CPU/req)
15,402
(80 MiB / 17.4% CPU · 33.9 µs CPU/req)
🥇 17,906
(105 MiB / 25.3% CPU · 42.3 µs CPU/req)
14,364
(114 MiB / 32% CPU · 66.9 µs CPU/req)
9,519
(166 MiB / 21.9% CPU · 69.0 µs CPU/req)
HTTP/2 · plain HTTP/2 · plain 10,070
(228 MiB / 4.4% CPU · 13.2 µs CPU/req)
Not possible (no H2 upstream) 🥇 32,271
(121 MiB / 29.4% CPU · 27.4 µs CPU/req)
12,111
(110 MiB / 14% CPU · 34.8 µs CPU/req)
13,339
(169 MiB / 8.3% CPU · 18.6 µs CPU/req)
HTTP/2 · plain HTTP/2 · TLS 🥇 20,563
(251 MiB / 16.9% CPU · 24.6 µs CPU/req)
Not possible (no H2 upstream) 16,800
(102 MiB / 24.1% CPU · 43.1 µs CPU/req)
6,953
(112 MiB / 15.2% CPU · 65.6 µs CPU/req)
9,670
(222 MiB / 10.3% CPU · 31.9 µs CPU/req)
HTTP/2 · plain HTTP/3 · QUIC 8,452
(208 MiB / 16.1% CPU · 57.2 µs CPU/req)
Not possible (no H3 upstream) Not possible (no H3 upstream) 4,598
(115 MiB / 17.3% CPU · 112.6 µs CPU/req)
🥇 10,963
(167 MiB / 22.7% CPU · 62.0 µs CPU/req)
HTTP/2 · TLS HTTP/1 · plain 11,200
(213 MiB / 16.4% CPU · 43.8 µs CPU/req)
7,670
(83 MiB / 9.1% CPU · 35.6 µs CPU/req)
14,900
(107 MiB / 21.3% CPU · 42.9 µs CPU/req)
6,002
(122 MiB / 18.8% CPU · 93.8 µs CPU/req)
🥇 15,402
(159 MiB / 18.5% CPU · 36.1 µs CPU/req)
HTTP/2 · TLS HTTP/1 · TLS 9,887
(200 MiB / 15.8% CPU · 47.9 µs CPU/req)
🥇 10,835
(89 MiB / 12.6% CPU · 35.0 µs CPU/req)
7,766
(111 MiB / 17% CPU · 65.8 µs CPU/req)
9,168
(123 MiB / 25.1% CPU · 82.3 µs CPU/req)
3,626
(199 MiB / 9.9% CPU · 82.0 µs CPU/req)
HTTP/2 · TLS HTTP/2 · plain 7,690
(251 MiB / 6.9% CPU · 26.8 µs CPU/req)
Not possible (no H2 upstream) 17,074
(114 MiB / 28% CPU · 49.1 µs CPU/req)
13,606
(121 MiB / 20.1% CPU · 44.4 µs CPU/req)
🥇 21,647
(260 MiB / 15.4% CPU · 21.3 µs CPU/req)
HTTP/2 · TLS HTTP/2 · TLS 5,276
(250 MiB / 6.3% CPU · 35.7 µs CPU/req)
Not possible (no H2 upstream) 🥇 11,851
(112 MiB / 23.2% CPU · 58.8 µs CPU/req)
8,169
(120 MiB / 14.9% CPU · 54.9 µs CPU/req)
7,293
(257 MiB / 8% CPU · 32.8 µs CPU/req)
HTTP/2 · TLS HTTP/3 · QUIC 10,936
(203 MiB / 23.2% CPU · 63.8 µs CPU/req)
Not possible (no H3 upstream) Not possible (no H3 upstream) 18,630
(123 MiB / 42% CPU · 67.6 µs CPU/req)
🥇 23,022
(175 MiB / 37.1% CPU · 48.4 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · plain 🥇 5,777
(145 MiB / 23.5% CPU · 121.9 µs CPU/req)
5,501
(86 MiB / 6.6% CPU · 35.8 µs CPU/req)
4,099
(105 MiB / 10.7% CPU · 78.0 µs CPU/req)
5,457
(126 MiB / 33.4% CPU · 183.5 µs CPU/req)
5,014
(249 MiB / 16.9% CPU · 100.9 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · TLS 6,138
(191 MiB / 25.5% CPU · 124.5 µs CPU/req)
4,947
(91 MiB / 7.8% CPU · 47.0 µs CPU/req)
🥇 10,604
(108 MiB / 20.3% CPU · 57.4 µs CPU/req)
5,362
(127 MiB / 39.1% CPU · 219.0 µs CPU/req)
2,660
(278 MiB / 13.9% CPU · 156.6 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · plain 3,671
(141 MiB / 10.9% CPU · 89.0 µs CPU/req)
Not possible (no H2 upstream) 🥇 14,293
(107 MiB / 14.9% CPU · 31.3 µs CPU/req)
11,338
(122 MiB / 39.7% CPU · 105.1 µs CPU/req)
9,863
(259 MiB / 21.2% CPU · 64.5 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 8,545
(143 MiB / 22.3% CPU · 78.3 µs CPU/req)
Not possible (no H2 upstream) 3,732
(108 MiB / 7.2% CPU · 57.5 µs CPU/req)
4,660
(124 MiB / 24.9% CPU · 160.4 µs CPU/req)
7,336
(281 MiB / 19.7% CPU · 80.5 µs CPU/req)
HTTP/3 · QUIC HTTP/3 · QUIC 6,443
(138 MiB / 24.6% CPU · 114.4 µs CPU/req)
Not possible (no H3 upstream) Not possible (no H3 upstream) 4,101
(123 MiB / 34% CPU · 248.7 µs CPU/req)
🥇 7,204
(258 MiB / 22.5% CPU · 93.6 µs CPU/req)

Editions (CLI / Plus / Intercept)

Note: twp-reverse-http1 and other library rows use Core with probe-tuned settings (no logging, no Via header, probe-warmed certs). Edition rows use titanium run -c twp.yaml product defaults — prefer the ÷baseline ratio column over absolute RPS. Inspector GUI is not spawnable in the harness; session-path overhead is twp-cli-intercept-http1 (route RequestHeaderSet transform). Pre-origin Plus middleware (CIDR/WAF/JWT/rate-limit/cache) runs on H1 terminate-lite without SessionEventArgs; a cache hit skips the origin. JWT caches successful bearer validations. CORS only adds response headers on the way out, so it stays on terminate-lite. Circuit breaker and idempotent retry have to see the request or the status code, so they stay on the session path and should land near the intercept row. Maintainer gate thresholds live under Maintainer notes.

Median of 3 repeats on ubuntu-latest @ 88187363 (Actions 38550348130). Warmup 2s / measure 8s; concurrency 8–64; sustain = median peak RPS among SLO-pass steps @ c=64. RPS cells show sustain; <sub> holds peak (when higher) plus (MiB / CPU%). The Gate column is that script's floor.

Arm Linux Lin÷ Gate
twp-cli-reverse-http1 vs library 64,175
(170 MiB / 49.3% CPU · 30.7 µs CPU/req)
1.04× ≥ 0.50×
twp-cli-reverse-http1-tls vs library TLS 37,304
(203 MiB / 49.4% CPU · 53.0 µs CPU/req)
1.00× ≥ 0.50×
twp-cli-reverse-http1-route vs CLI 32,919
(169 MiB / 50.5% CPU · 61.4 µs CPU/req)
0.99× ≥ 0.50×
twp-cli-plus-base-http1 vs CLI 33,573
(173 MiB / 49.8% CPU · 59.3 µs CPU/req)
0.99× ≥ 0.50×
twp-cli-plus-cache-http1 (cold) vs CLI 56,233
(172 MiB / 47.5% CPU · 33.8 µs CPU/req)
1.72× ≥ 0.50×
twp-cli-intercept-http1 vs CLI 24,552
(177 MiB / 52.9% CPU · 86.2 µs CPU/req)
0.74× ≥ 0.50×
twp-cli-plus-waf-http1 vs CLI 32,532
(171 MiB / 50.6% CPU · 62.2 µs CPU/req)
0.97× ≥ 0.50×
twp-cli-plus-cidr-http1 vs CLI 32,877
(171 MiB / 50.7% CPU · 61.6 µs CPU/req)
1.01× ≥ 0.50×
twp-cli-plus-jwt-http1 vs CLI 44,556
(193 MiB / 49.5% CPU · 44.4 µs CPU/req)
0.93× ≥ 0.50×
twp-cli-plus-ratelimit-http1 vs CLI 32,368
(173 MiB / 51.0% CPU · 63.0 µs CPU/req)
1.00× ≥ 0.50×
twp-cli-plus-resilience-http1 vs CLI 46,996
(184 MiB / 49.0% CPU · 41.7 µs CPU/req)
0.99× ≥ 0.50×
twp-cli-plus-discovery-file-http1 vs CLI 33,786
(173 MiB / 50.3% CPU · 59.6 µs CPU/req)
1.02× ≥ 0.50×
twp-cli-plus-metrics-scrape-http1 vs CLI 33,347
(174 MiB / 49.8% CPU · 59.7 µs CPU/req)
0.99× ≥ 0.50×
twp-cli-plus-cache-hit-http1 vs cache cold 57,046
(172 MiB / 48.5% CPU · 34.0 µs CPU/req)
1.01× ≥ 0.50×
twp-cli-plus-cors-http1 vs CLI 30,799
(173 MiB / 50.4% CPU · 65.4 µs CPU/req)
0.92× ≥ 0.50×
twp-cli-plus-circuit-http1 vs CLI 26,008
(180 MiB / 51.8% CPU · 79.7 µs CPU/req)
0.78× ≥ 0.50×
twp-cli-plus-retry-http1 vs CLI 27,089
(180 MiB / 51.2% CPU · 75.6 µs CPU/req)
0.79× ≥ 0.50×
twp-cli-static-http1 vs CLI 54,140
(169 MiB / 50.7% CPU · 37.5 µs CPU/req)
1.66× ≥ 0.50×
twp-cli-logging-http1 vs CLI 55,368
(235 MiB / 48.8% CPU · 35.3 µs CPU/req)
1.00× ≥ 0.50×
twp-cli-lb-leasttime-http1 vs route 45,938
(194 MiB / 50.3% CPU · 43.8 µs CPU/req)
0.96× ≥ 0.50×
twp-cli-dialect-twp-http1 vs CLI 65,370
(163 MiB / 49.1% CPU · 30.0 µs CPU/req)
1.01× ≥ 0.50×

validate-edition-gates.ps1 floors are 0.50×. Each ÷ column uses the two arms from the same job. Circuit breaker and idempotent retry stay on the session path, so a ratio near intercept is expected. Laptop smoke ratios stay on Performance Local Lab — Editions.

Heavier reverse workloads

Same runners and harness as the tiny-GET tables, but with larger bodies, POST, lossy links, TLS cost, and architecture-sensitive paths (slow consumer / early response / duplex). PUT with the same body is the same proxy work as POST; DELETE with no body matches GET — only POST is published. Bodies/POST/lossy stay half-duplex. Laptop numbers are on Performance Local Lab. How maintainers refresh these tables (modes, shards, paste scripts) is under Maintainer notes. Larger-body check: 64 / 256 KiB H2 TLS→H2 TLS is where body copy dominates headers — ratios vs YARP are in the tables below (@ 88187363), unlike the tiny-GET H2↔H2 medals above. Those cells also show goodput (MiB/s) and µs CPU/req, and the medal on those rows follows goodput.

Lossy link = userspace delay/drop shim (not kernel netem): TCP gets per-buffer delay + occasional whole-connection stalls (honest head-of-line for multiplexed HTTP/2); UDP is loss% only (no per-datagram delay) + drops (QUIC / MsQuic-safe). Lossy tables publish HTTP/1, HTTP/2, and HTTP/3.

Windows — heavier reverse GET (64 KiB / 256 KiB)

Median of 3 repeats on windows-latest @ 88187363. Source: Actions 38550291997 (compare-bodies). Warmup 2s / measure 8s. RPS cells include (MiB / CPU%) footprints.

Not possible: HAProxy and Envoy columns are omitted (no official Windows port).

Body Client Origin TWP nginx YARP
64 KiB HTTP/1 · TLS HTTP/1 · plain 🥇 9,224
(91 MiB / 45.5% CPU · 577 MiB/s · 197.5 µs CPU/req)
622
(142 MiB / 24.8% CPU · 39 MiB/s · 1590.9 µs CPU/req)
8,011
(138 MiB / 45.9% CPU · 501 MiB/s · 229.1 µs CPU/req)
64 KiB HTTP/2 · TLS HTTP/1 · plain 🥇 8,238
(193 MiB / 49.8% CPU · 515 MiB/s · 241.9 µs CPU/req)
578
(142 MiB / 24.8% CPU · 36 MiB/s · 1719.5 µs CPU/req)
8,024
(127 MiB / 53.8% CPU · 502 MiB/s · 268.4 µs CPU/req)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 4,878
(141 MiB / 44.8% CPU · 305 MiB/s · 367.6 µs CPU/req)
Not possible (no QUIC) 2,504
(183 MiB / 51.9% CPU · 156 MiB/s · 828.9 µs CPU/req)
256 KiB HTTP/1 · TLS HTTP/1 · plain 🥇 3,063
(138 MiB / 40.5% CPU · 766 MiB/s · 529.6 µs CPU/req)
195
(142 MiB / 24.3% CPU · 49 MiB/s · 4982.1 µs CPU/req)
2,736
(136 MiB / 43.9% CPU · 684 MiB/s · 642.0 µs CPU/req)
256 KiB HTTP/2 · TLS HTTP/1 · plain 2,406
(185 MiB / 50.5% CPU · 601 MiB/s · 839.3 µs CPU/req)
154
(143 MiB / 24.7% CPU · 38 MiB/s · 6426.0 µs CPU/req)
🥇 2,696
(132 MiB / 47.0% CPU · 674 MiB/s · 697.3 µs CPU/req)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 339
(90 MiB / 33.8% CPU · 85 MiB/s · 3990.6 µs CPU/req)
Not possible (no QUIC) 146
(125 MiB / 49.8% CPU · 36 MiB/s · 13668.0 µs CPU/req)
64 KiB HTTP/2 · plain HTTP/1 · plain 🥇 12,817
(168 MiB / 43.1% CPU · 801 MiB/s · 134.4 µs CPU/req)
2,037
(127 MiB / 24.8% CPU · 127 MiB/s · 486.4 µs CPU/req)
11,420
(114 MiB / 46.1% CPU · 714 MiB/s · 161.5 µs CPU/req)
64 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 8,152
(170 MiB / 45.8% CPU · 510 MiB/s · 224.8 µs CPU/req)
Not possible 7,312
(140 MiB / 53.3% CPU · 457 MiB/s · 291.4 µs CPU/req)
64 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 6,073
(177 MiB / 41.1% CPU · 380 MiB/s · 270.5 µs CPU/req)
Not possible 5,128
(144 MiB / 49.3% CPU · 320 MiB/s · 384.9 µs CPU/req)
64 KiB HTTP/3 · QUIC HTTP/2 · TLS 🥇 3,838
(138 MiB / 42.8% CPU · 240 MiB/s · 446.2 µs CPU/req)
Not possible 2,093
(182 MiB / 50.3% CPU · 131 MiB/s · 962.0 µs CPU/req)
64 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 3,439
(145 MiB / 43.5% CPU · 215 MiB/s · 505.4 µs CPU/req)
Not possible (no QUIC) 1,856
(187 MiB / 52.6% CPU · 116 MiB/s · 1133.6 µs CPU/req)
256 KiB HTTP/2 · plain HTTP/1 · plain 🥇 4,263
(180 MiB / 35.5% CPU · 1,066 MiB/s · 332.6 µs CPU/req)
772
(127 MiB / 24.6% CPU · 193 MiB/s · 1276.5 µs CPU/req)
4,002
(116 MiB / 39.6% CPU · 1,001 MiB/s · 396.1 µs CPU/req)
256 KiB HTTP/2 · TLS HTTP/2 · plain 2,186
(203 MiB / 46.6% CPU · 547 MiB/s · 852.4 µs CPU/req)
Not possible 🥇 2,221
(169 MiB / 46.6% CPU · 555 MiB/s · 839.8 µs CPU/req)
256 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 1,673
(201 MiB / 42.2% CPU · 418 MiB/s · 1008.1 µs CPU/req)
Not possible 1,410
(148 MiB / 45.9% CPU · 352 MiB/s · 1302.3 µs CPU/req)
256 KiB HTTP/3 · QUIC HTTP/2 · TLS 400
(139 MiB / 49.1% CPU · 100 MiB/s · 4903.5 µs CPU/req)
Not possible 🥇 1,284
(182 MiB / 48.6% CPU · 321 MiB/s · 1512.5 µs CPU/req)
256 KiB HTTP/3 · QUIC HTTP/1 · TLS 111
(peak 120 · 103 MiB / 42.7% CPU · 28 MiB/s · 15342.3 µs CPU/req)
Not possible (no QUIC) 🥇 239
(162 MiB / 50.1% CPU · 60 MiB/s · 8390.4 µs CPU/req)

nginx/Windows collapses on large reverse bodies in this harness; treat as same-OS only. Prefer the ratio columns; absolute RPS swings by VM.

Linux — heavier reverse GET (64 KiB / 256 KiB)

Median of 3 repeats @ 88187363. Source: Actions 38550291997 (compare-bodies). Warmup 2s / measure 8s.

Body Client Origin TWP nginx HAProxy Envoy YARP
64 KiB HTTP/1 · TLS HTTP/1 · plain 8,159
(127 MiB / 44.0% CPU · 510 MiB/s · 215.7 µs CPU/req)
5,438
(98 MiB / 51.7% CPU · 340 MiB/s · 380.1 µs CPU/req)
🥇 8,202
(87 MiB / 41.0% CPU · 513 MiB/s · 200.2 µs CPU/req)
7,105
(131 MiB / 45.6% CPU · 444 MiB/s · 256.9 µs CPU/req)
6,344
(160 MiB / 48.5% CPU · 397 MiB/s · 305.8 µs CPU/req)
64 KiB HTTP/2 · TLS HTTP/1 · plain 14,150
(240 MiB / 42.9% CPU · 884 MiB/s · 121.2 µs CPU/req)
8,886
(102 MiB / 49.1% CPU · 555 MiB/s · 220.8 µs CPU/req)
12,198
(88 MiB / 46.0% CPU · 762 MiB/s · 150.7 µs CPU/req)
🥇 15,030
(134 MiB / 44.2% CPU · 939 MiB/s · 117.8 µs CPU/req)
12,355
(159 MiB / 45.9% CPU · 772 MiB/s · 148.7 µs CPU/req)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 8,368
(214 MiB / 46.0% CPU · 523 MiB/s · 220.0 µs CPU/req)
3,214
(117 MiB / 22.1% CPU · 201 MiB/s · 275.2 µs CPU/req)
🥇 8,604
(95 MiB / 36.4% CPU · 538 MiB/s · 169.0 µs CPU/req)
6,424
(138 MiB / 49.0% CPU · 402 MiB/s · 305.0 µs CPU/req)
5,083
(239 MiB / 51.2% CPU · 318 MiB/s · 403.2 µs CPU/req)
256 KiB HTTP/1 · TLS HTTP/1 · plain 2,633
(133 MiB / 36.9% CPU · 658 MiB/s · 561.0 µs CPU/req)
1,688
(99 MiB / 53.1% CPU · 422 MiB/s · 1258.1 µs CPU/req)
🥇 2,839
(87 MiB / 32.7% CPU · 710 MiB/s · 460.5 µs CPU/req)
2,632
(146 MiB / 33.8% CPU · 658 MiB/s · 514.2 µs CPU/req)
2,113
(170 MiB / 45.9% CPU · 528 MiB/s · 869.5 µs CPU/req)
256 KiB HTTP/2 · TLS HTTP/1 · plain 2,064
(201 MiB / 41.2% CPU · 516 MiB/s · 799.0 µs CPU/req)
1,482
(100 MiB / 49.9% CPU · 371 MiB/s · 1346.1 µs CPU/req)
2,081
(85 MiB / 41.1% CPU · 520 MiB/s · 789.5 µs CPU/req)
🥇 2,416
(151 MiB / 36.0% CPU · 604 MiB/s · 596.0 µs CPU/req)
1,854
(168 MiB / 46.1% CPU · 464 MiB/s · 995.3 µs CPU/req)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 1,398
(176 MiB / 52.4% CPU · 349 MiB/s · 1499.2 µs CPU/req)
702
(113 MiB / 37.4% CPU · 175 MiB/s · 2133.7 µs CPU/req)
🥇 1,598
(98 MiB / 36.6% CPU · 400 MiB/s · 917.0 µs CPU/req)
1,352
(peak 1,381 · 143 MiB / 43.4% CPU · 338 MiB/s · 1282.6 µs CPU/req)
1,202
(226 MiB / 52.8% CPU · 301 MiB/s · 1758.1 µs CPU/req)
64 KiB HTTP/2 · plain HTTP/1 · plain 14,662
(226 MiB / 43.6% CPU · 916 MiB/s · 118.9 µs CPU/req)
11,305
(79 MiB / 43.8% CPU · 707 MiB/s · 154.9 µs CPU/req)
🥇 14,980
(74 MiB / 41.7% CPU · 936 MiB/s · 111.3 µs CPU/req)
14,696
(124 MiB / 45.8% CPU · 919 MiB/s · 124.7 µs CPU/req)
13,813
(142 MiB / 45.1% CPU · 863 MiB/s · 130.6 µs CPU/req)
64 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 10,906
(211 MiB / 37.9% CPU · 682 MiB/s · 139.0 µs CPU/req)
Not possible 8,244
(91 MiB / 44.5% CPU · 515 MiB/s · 215.9 µs CPU/req)
9,658
(136 MiB / 44.1% CPU · 604 MiB/s · 182.6 µs CPU/req)
8,041
(206 MiB / 48.5% CPU · 503 MiB/s · 241.1 µs CPU/req)
64 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 4,949
(211 MiB / 41.7% CPU · 309 MiB/s · 337.2 µs CPU/req)
Not possible 4,157
(89 MiB / 44.7% CPU · 260 MiB/s · 430.1 µs CPU/req)
4,555
(141 MiB / 43.5% CPU · 285 MiB/s · 382.2 µs CPU/req)
3,546
(172 MiB / 46.7% CPU · 222 MiB/s · 526.6 µs CPU/req)
64 KiB HTTP/3 · QUIC HTTP/2 · TLS 3,447
(181 MiB / 46.1% CPU · 215 MiB/s · 534.7 µs CPU/req)
Not possible 🥇 3,722
(97 MiB / 41.1% CPU · 233 MiB/s · 442.3 µs CPU/req)
2,848
(146 MiB / 49.7% CPU · 178 MiB/s · 698.6 µs CPU/req)
2,488
(245 MiB / 50.4% CPU · 155 MiB/s · 810.8 µs CPU/req)
64 KiB HTTP/3 · QUIC HTTP/1 · TLS 🥇 3,788
(213 MiB / 46.8% CPU · 237 MiB/s · 494.1 µs CPU/req)
2,326
(118 MiB / 39.2% CPU · 145 MiB/s · 673.5 µs CPU/req)
3,772
(97 MiB / 40.9% CPU · 236 MiB/s · 433.7 µs CPU/req)
3,194
(139 MiB / 48.8% CPU · 200 MiB/s · 610.6 µs CPU/req)
2,807
(258 MiB / 51.3% CPU · 175 MiB/s · 730.6 µs CPU/req)
256 KiB HTTP/2 · plain HTTP/1 · plain 4,636
(196 MiB / 31.6% CPU · 1,159 MiB/s · 272.5 µs CPU/req)
3,377
(79 MiB / 47.7% CPU · 844 MiB/s · 564.6 µs CPU/req)
4,947
(71 MiB / 30.6% CPU · 1,237 MiB/s · 247.8 µs CPU/req)
🥇 5,253
(146 MiB / 29.8% CPU · 1,313 MiB/s · 226.7 µs CPU/req)
4,679
(149 MiB / 34.6% CPU · 1,170 MiB/s · 296.2 µs CPU/req)
256 KiB HTTP/2 · TLS HTTP/2 · plain 🥇 2,067
(239 MiB / 37.6% CPU · 517 MiB/s · 728.5 µs CPU/req)
Not possible 1,919
(99 MiB / 38.9% CPU · 480 MiB/s · 809.9 µs CPU/req)
1,995
(144 MiB / 37.1% CPU · 499 MiB/s · 744.1 µs CPU/req)
1,504
(204 MiB / 45.1% CPU · 376 MiB/s · 1200.0 µs CPU/req)
256 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 1,950
(249 MiB / 37.7% CPU · 487 MiB/s · 773.5 µs CPU/req)
Not possible 1,890
(98 MiB / 39.3% CPU · 472 MiB/s · 832.1 µs CPU/req)
1,940
(146 MiB / 38.6% CPU · 485 MiB/s · 794.9 µs CPU/req)
1,434
(193 MiB / 43.4% CPU · 358 MiB/s · 1210.3 µs CPU/req)
256 KiB HTTP/3 · QUIC HTTP/2 · TLS 749
(194 MiB / 54.6% CPU · 187 MiB/s · 2916.6 µs CPU/req)
Not possible 🥇 1,312
(107 MiB / 36.1% CPU · 328 MiB/s · 1100.9 µs CPU/req)
1,103
(154 MiB / 45.0% CPU · 276 MiB/s · 1630.6 µs CPU/req)
886
(253 MiB / 47.1% CPU · 221 MiB/s · 2127.6 µs CPU/req)
256 KiB HTTP/3 · QUIC HTTP/1 · TLS 1,086
(193 MiB / 49.0% CPU · 271 MiB/s · 1804.8 µs CPU/req)
663
(118 MiB / 40.2% CPU · 166 MiB/s · 2424.4 µs CPU/req)
🥇 1,168
(98 MiB / 39.0% CPU · 292 MiB/s · 1335.7 µs CPU/req)
1,124
(152 MiB / 44.0% CPU · 281 MiB/s · 1568.0 µs CPU/req)
895
(255 MiB / 51.3% CPU · 224 MiB/s · 2290.6 µs CPU/req)

Prefer the ratio columns; absolute RPS swings by VM.

macOS — heavier reverse GET (64 KiB / 256 KiB)

Median of 3 repeats on macos-15 (Apple Silicon M1, 3-core / 7 GB) @ 88187363. Source: Actions 38550291997 (compare-bodies). Warmup 2s / measure 8s.

Body Client Origin TWP nginx HAProxy Envoy YARP
64 KiB HTTP/1 · TLS HTTP/1 · plain 3,740
(192 MiB / 17.1% CPU · 234 MiB/s · 137.0 µs CPU/req)
4,809
(84 MiB / 18.5% CPU · 301 MiB/s · 115.5 µs CPU/req)
🥇 6,451
(120 MiB / 20.5% CPU · 403 MiB/s · 95.2 µs CPU/req)
4,944
(134 MiB / 29.6% CPU · 309 MiB/s · 179.3 µs CPU/req)
3,247
(182 MiB / 25.7% CPU · 203 MiB/s · 237.6 µs CPU/req)
64 KiB HTTP/2 · TLS HTTP/1 · plain 2,701
(487 MiB / 16.7% CPU · 169 MiB/s · 185.6 µs CPU/req)
🥇 4,722
(87 MiB / 18.3% CPU · 295 MiB/s · 116.1 µs CPU/req)
4,408
(122 MiB / 26.5% CPU · 276 MiB/s · 180.4 µs CPU/req)
3,614
(134 MiB / 18.5% CPU · 226 MiB/s · 153.4 µs CPU/req)
1,874
(176 MiB / 14.9% CPU · 117 MiB/s · 238.6 µs CPU/req)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 1,559
(279 MiB / 30.1% CPU · 97 MiB/s · 578.8 µs CPU/req)
🥇 1,718
(peak 1,951 · 95 MiB / 22.7% CPU · 107 MiB/s · 395.6 µs CPU/req)
1,292
(126 MiB / 27.9% CPU · 81 MiB/s · 647.6 µs CPU/req)
1,477
(133 MiB / 50.3% CPU · 92 MiB/s · 1021.8 µs CPU/req)
792
(437 MiB / 27.9% CPU · 49 MiB/s · 1057.6 µs CPU/req)
256 KiB HTTP/1 · TLS HTTP/1 · plain 1,913
(210 MiB / 24.2% CPU · 478 MiB/s · 379.6 µs CPU/req)
1,760
(84 MiB / 18.9% CPU · 440 MiB/s · 321.7 µs CPU/req)
🥇 2,238
(117 MiB / 25.3% CPU · 559 MiB/s · 339.6 µs CPU/req)
1,933
(155 MiB / 22.5% CPU · 483 MiB/s · 349.7 µs CPU/req)
1,463
(183 MiB / 31.3% CPU · 366 MiB/s · 642.3 µs CPU/req)
256 KiB HTTP/2 · TLS HTTP/1 · plain 1,089
(426 MiB / 18.8% CPU · 272 MiB/s · 517.5 µs CPU/req)
1,521
(86 MiB / 21.7% CPU · 380 MiB/s · 428.3 µs CPU/req)
🥇 1,971
(120 MiB / 30.6% CPU · 493 MiB/s · 466.3 µs CPU/req)
1,777
(154 MiB / 27.2% CPU · 444 MiB/s · 458.7 µs CPU/req)
1,439
(183 MiB / 30.8% CPU · 360 MiB/s · 641.4 µs CPU/req)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 329
(183 MiB / 28.8% CPU · 82 MiB/s · 2623.3 µs CPU/req)
🥇 630
(95 MiB / 27.8% CPU · 158 MiB/s · 1323.2 µs CPU/req)
246
(peak 428 · 105 MiB / 25.2% CPU · 62 MiB/s · 3064.5 µs CPU/req)
367
(150 MiB / 51.5% CPU · 92 MiB/s · 4203.1 µs CPU/req)
96
(peak 152 · 284 MiB / 18.1% CPU · 24 MiB/s · 5677.8 µs CPU/req)
64 KiB HTTP/2 · plain HTTP/1 · plain 2,341
(466 MiB / 13.0% CPU · 146 MiB/s · 166.3 µs CPU/req)
3,687
(73 MiB / 11.6% CPU · 230 MiB/s · 94.7 µs CPU/req)
🥇 3,774
(109 MiB / 18.8% CPU · 236 MiB/s · 149.1 µs CPU/req)
3,571
(122 MiB / 19.6% CPU · 223 MiB/s · 165.0 µs CPU/req)
2,058
(212 MiB / 13.8% CPU · 129 MiB/s · 200.7 µs CPU/req)
64 KiB HTTP/2 · TLS HTTP/2 · plain 2,430
(370 MiB / 13.1% CPU · 152 MiB/s · 161.5 µs CPU/req)
Not possible 🥇 3,285
(126 MiB / 25.0% CPU · 205 MiB/s · 228.2 µs CPU/req)
2,631
(126 MiB / 18.5% CPU · 164 MiB/s · 210.9 µs CPU/req)
3,190
(263 MiB / 23.1% CPU · 199 MiB/s · 216.8 µs CPU/req)
64 KiB HTTP/2 · TLS HTTP/2 · TLS 2,725
(407 MiB / 21.0% CPU · 170 MiB/s · 231.4 µs CPU/req)
Not possible 🥇 4,009
(122 MiB / 30.9% CPU · 251 MiB/s · 230.8 µs CPU/req)
3,328
(126 MiB / 23.1% CPU · 208 MiB/s · 208.1 µs CPU/req)
1,803
(249 MiB / 21.6% CPU · 113 MiB/s · 359.5 µs CPU/req)
64 KiB HTTP/3 · QUIC HTTP/2 · TLS 🥇 1,971
(235 MiB / 27.2% CPU · 123 MiB/s · 413.9 µs CPU/req)
Not possible 1,448
(131 MiB / 26.1% CPU · 90 MiB/s · 539.8 µs CPU/req)
1,573
(130 MiB / 46.1% CPU · 98 MiB/s · 879.0 µs CPU/req)
907
(452 MiB / 23.4% CPU · 57 MiB/s · 774.3 µs CPU/req)
64 KiB HTTP/3 · QUIC HTTP/1 · TLS 1,697
(251 MiB / 29.6% CPU · 106 MiB/s · 523.7 µs CPU/req)
🥇 2,292
(101 MiB / 24.4% CPU · 143 MiB/s · 319.0 µs CPU/req)
1,655
(129 MiB / 31.4% CPU · 103 MiB/s · 569.7 µs CPU/req)
1,690
(132 MiB / 45.2% CPU · 106 MiB/s · 803.0 µs CPU/req)
923
(400 MiB / 23.1% CPU · 58 MiB/s · 750.7 µs CPU/req)
256 KiB HTTP/2 · plain HTTP/1 · plain 1,439
(463 MiB / 13.5% CPU · 360 MiB/s · 281.0 µs CPU/req)
🥇 3,278
(73 MiB / 17.7% CPU · 819 MiB/s · 161.7 µs CPU/req)
2,845
(109 MiB / 20.9% CPU · 711 MiB/s · 220.5 µs CPU/req)
2,510
(145 MiB / 19.3% CPU · 628 MiB/s · 230.5 µs CPU/req)
1,688
(212 MiB / 17.7% CPU · 422 MiB/s · 315.3 µs CPU/req)
256 KiB HTTP/2 · TLS HTTP/2 · plain 1,401
(699 MiB / 28.0% CPU · 350 MiB/s · 599.1 µs CPU/req)
Not possible 🥇 1,635
(140 MiB / 28.9% CPU · 409 MiB/s · 530.8 µs CPU/req)
1,588
(137 MiB / 21.1% CPU · 397 MiB/s · 399.1 µs CPU/req)
1,185
(294 MiB / 29.2% CPU · 296 MiB/s · 739.9 µs CPU/req)
256 KiB HTTP/2 · TLS HTTP/2 · TLS 🥇 1,377
(543 MiB / 29.2% CPU · 344 MiB/s · 635.3 µs CPU/req)
Not possible 1,355
(134 MiB / 32.2% CPU · 339 MiB/s · 712.5 µs CPU/req)
1,020
(130 MiB / 25.8% CPU · 255 MiB/s · 758.0 µs CPU/req)
975
(270 MiB / 29.5% CPU · 244 MiB/s · 907.4 µs CPU/req)
256 KiB HTTP/3 · QUIC HTTP/2 · TLS 90
(peak 176 · 287 MiB / 25.3% CPU · 22 MiB/s · 8477.1 µs CPU/req)
Not possible 335
(peak 482 · 114 MiB / 26.2% CPU · 84 MiB/s · 2343.6 µs CPU/req)
🥇 368
(144 MiB / 48.7% CPU · 92 MiB/s · 3967.7 µs CPU/req)
253
(531 MiB / 30.4% CPU · 63 MiB/s · 3601.7 µs CPU/req)
256 KiB HTTP/3 · QUIC HTTP/1 · TLS 290
(peak 414 · 202 MiB / 31.3% CPU · 73 MiB/s · 3232.4 µs CPU/req)
🥇 562
(99 MiB / 26.7% CPU · 141 MiB/s · 1426.4 µs CPU/req)
438
(peak 525 · 108 MiB / 28.1% CPU · 110 MiB/s · 1926.3 µs CPU/req)
454
(151 MiB / 47.3% CPU · 114 MiB/s · 3122.1 µs CPU/req)
190
(398 MiB / 23.4% CPU · 48 MiB/s · 3678.7 µs CPU/req)

Windows — POST 64 KiB request + 64 KiB response

Median of 3 repeats on windows-latest @ 88187363. Source: Actions 38550300822 (compare-post).

Not possible: HAProxy and Envoy columns are omitted (no official Windows port).

Client Origin TWP nginx YARP
HTTP/1 · TLS HTTP/1 · plain 🥇 5,990
(99 MiB / 44.7% CPU · 749 MiB/s · 298.4 µs CPU/req)
394
(143 MiB / 24.9% CPU · 49 MiB/s · 2534.9 µs CPU/req)
4,238
(136 MiB / 57.4% CPU · 530 MiB/s · 541.6 µs CPU/req)
HTTP/2 · TLS HTTP/1 · plain 🥇 3,953
(201 MiB / 46.2% CPU · 494 MiB/s · 467.9 µs CPU/req)
357
(147 MiB / 24.8% CPU · 45 MiB/s · 2772.3 µs CPU/req)
3,771
(136 MiB / 47.2% CPU · 471 MiB/s · 500.2 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · plain 🥇 2,219
(184 MiB / 46.0% CPU · 277 MiB/s · 829.3 µs CPU/req)
Not possible (no QUIC) 1,497
(207 MiB / 52.8% CPU · 187 MiB/s · 1411.5 µs CPU/req)
HTTP/2 · plain HTTP/1 · plain 🥇 6,612
(198 MiB / 43.4% CPU · 827 MiB/s · 262.5 µs CPU/req)
1,781
(131 MiB / 24.8% CPU · 223 MiB/s · 556.4 µs CPU/req)
6,280
(114 MiB / 47.0% CPU · 785 MiB/s · 299.6 µs CPU/req)
HTTP/2 · TLS HTTP/2 · plain 🥇 4,452
(167 MiB / 38.3% CPU · 556 MiB/s · 344.3 µs CPU/req)
Not possible 3,587
(151 MiB / 46.6% CPU · 448 MiB/s · 519.3 µs CPU/req)
HTTP/2 · TLS HTTP/2 · TLS 🥇 3,558
(166 MiB / 39.0% CPU · 445 MiB/s · 438.8 µs CPU/req)
Not possible 2,851
(149 MiB / 48.3% CPU · 356 MiB/s · 678.0 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 2,596
(199 MiB / 44.4% CPU · 325 MiB/s · 684.4 µs CPU/req)
Not possible 1,585
(221 MiB / 51.6% CPU · 198 MiB/s · 1303.1 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 1,684
(162 MiB / 43.2% CPU · 211 MiB/s · 1024.9 µs CPU/req)
Not possible (no QUIC) 1,262
(207 MiB / 52.4% CPU · 158 MiB/s · 1661.1 µs CPU/req)

TWP leads H2→H1 POST (about 1.1–1.3× YARP) and H3 POST (about 1.0–1.1× YARP). H2 TLS→H2 TLS POST sustain is about 1.2–1.4× YARP.

Linux — POST 64 KiB request + 64 KiB response

Median of 3 repeats @ 88187363. Source: Actions 38550300822 (compare-post).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 4,912
(136 MiB / 45.1% CPU · 614 MiB/s · 367.6 µs CPU/req)
3,826
(99 MiB / 50.1% CPU · 478 MiB/s · 523.7 µs CPU/req)
🥇 5,131
(90 MiB / 41.8% CPU · 641 MiB/s · 325.9 µs CPU/req)
5,078
(134 MiB / 41.5% CPU · 635 MiB/s · 326.6 µs CPU/req)
3,256
(176 MiB / 55.0% CPU · 407 MiB/s · 675.6 µs CPU/req)
HTTP/2 · TLS HTTP/1 · plain 3,854
(269 MiB / 46.6% CPU · 482 MiB/s · 483.7 µs CPU/req)
3,583
(106 MiB / 44.5% CPU · 448 MiB/s · 496.2 µs CPU/req)
🥇 4,067
(91 MiB / 43.3% CPU · 508 MiB/s · 426.2 µs CPU/req)
3,856
(peak 4,641 · 126 MiB / 40.6% CPU · 482 MiB/s · 421.6 µs CPU/req)
3,502
(177 MiB / 50.2% CPU · 438 MiB/s · 573.0 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · plain 🥇 2,891
(224 MiB / 47.7% CPU · 361 MiB/s · 659.5 µs CPU/req)
850
(111 MiB / 44.1% CPU · 106 MiB/s · 2076.2 µs CPU/req)
2,481
(93 MiB / 44.1% CPU · 310 MiB/s · 711.9 µs CPU/req)
0
(131 MiB / 0.3% CPU)
2,111
(256 MiB / 49.7% CPU · 264 MiB/s · 942.5 µs CPU/req)
HTTP/2 · plain HTTP/1 · plain 5,706
(255 MiB / 44.1% CPU · 713 MiB/s · 308.9 µs CPU/req)
4,807
(86 MiB / 45.3% CPU · 601 MiB/s · 376.7 µs CPU/req)
🥇 6,132
(77 MiB / 40.1% CPU · 767 MiB/s · 261.4 µs CPU/req)
4,386
(peak 5,669 · 117 MiB / 44.6% CPU · 548 MiB/s · 406.6 µs CPU/req)
5,289
(157 MiB / 48.6% CPU · 661 MiB/s · 368.0 µs CPU/req)
HTTP/2 · TLS HTTP/2 · plain 8,148
(219 MiB / 35.1% CPU · 1,018 MiB/s · 172.4 µs CPU/req)
Not possible 6,789
(93 MiB / 41.3% CPU · 849 MiB/s · 243.3 µs CPU/req)
🥇 8,353
(135 MiB / 39.4% CPU · 1,044 MiB/s · 188.8 µs CPU/req)
5,511
(197 MiB / 47.7% CPU · 689 MiB/s · 346.0 µs CPU/req)
HTTP/2 · TLS HTTP/2 · TLS 🥇 6,055
(209 MiB / 37.5% CPU · 757 MiB/s · 247.5 µs CPU/req)
Not possible 4,983
(92 MiB / 42.8% CPU · 623 MiB/s · 343.1 µs CPU/req)
5,843
(142 MiB / 41.3% CPU · 730 MiB/s · 282.8 µs CPU/req)
4,372
(195 MiB / 46.3% CPU · 546 MiB/s · 423.3 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 2,753
(244 MiB / 42.9% CPU · 344 MiB/s · 623.8 µs CPU/req)
Not possible 2,498
(95 MiB / 39.5% CPU · 312 MiB/s · 632.3 µs CPU/req)
0
(130 MiB / 0.2% CPU)
2,034
(260 MiB / 48.8% CPU · 254 MiB/s · 958.6 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 2,178
(237 MiB / 47.2% CPU · 272 MiB/s · 866.7 µs CPU/req)
852
(114 MiB / 51.7% CPU · 107 MiB/s · 2427.2 µs CPU/req)
1,957
(96 MiB / 43.8% CPU · 245 MiB/s · 895.1 µs CPU/req)
0
(132 MiB / 0.4% CPU)
1,792
(259 MiB / 49.8% CPU · 224 MiB/s · 1112.2 µs CPU/req)

Linux nginx H1/H2/H3 POST completed (nginx.org mainline). TWP÷YARP H1 ≈ 1.5×; H2→H1 ≈ 1.2×; H3 ≈ 1.1×. TWP÷nginx H3 ≈ 6×.

macOS — POST 64 KiB request + 64 KiB response

Median of 3 repeats on macos-15 (Apple Silicon M1, 3-core / 7 GB) @ 88187363. Source: Actions 38550300822 (compare-post).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 2,939
(241 MiB / 25.1% CPU · 367 MiB/s · 256.1 µs CPU/req)
3,017
(89 MiB / 19.0% CPU · 377 MiB/s · 188.7 µs CPU/req)
🥇 3,944
(120 MiB / 29.0% CPU · 493 MiB/s · 220.8 µs CPU/req)
3,086
(133 MiB / 28.6% CPU · 386 MiB/s · 277.7 µs CPU/req)
1,791
(214 MiB / 31.9% CPU · 224 MiB/s · 535.3 µs CPU/req)
HTTP/2 · TLS HTTP/1 · plain 1,548
(537 MiB / 18.9% CPU · 194 MiB/s · 366.4 µs CPU/req)
2,346
(99 MiB / 19.0% CPU · 293 MiB/s · 243.3 µs CPU/req)
🥇 2,440
(124 MiB / 27.1% CPU · 305 MiB/s · 333.9 µs CPU/req)
1,255
(peak 2,646 · 121 MiB / 17.2% CPU · 157 MiB/s · 410.3 µs CPU/req)
1,574
(178 MiB / 23.9% CPU · 197 MiB/s · 454.5 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · plain 🥇 1,255
(308 MiB / 29.3% CPU · 157 MiB/s · 701.5 µs CPU/req)
1,154
(95 MiB / 24.2% CPU · 144 MiB/s · 627.9 µs CPU/req)
887
(119 MiB / 29.1% CPU · 111 MiB/s · 985.8 µs CPU/req)
1,055
(136 MiB / 46.9% CPU · 132 MiB/s · 1333.2 µs CPU/req)
597
(437 MiB / 24.6% CPU · 75 MiB/s · 1234.8 µs CPU/req)
HTTP/2 · plain HTTP/1 · plain 2,545
(492 MiB / 22.0% CPU · 318 MiB/s · 258.9 µs CPU/req)
🥇 3,354
(85 MiB / 16.6% CPU · 419 MiB/s · 148.1 µs CPU/req)
3,182
(111 MiB / 21.5% CPU · 398 MiB/s · 202.8 µs CPU/req)
2,372
(126 MiB / 20.1% CPU · 297 MiB/s · 253.6 µs CPU/req)
1,930
(240 MiB / 21.8% CPU · 241 MiB/s · 339.3 µs CPU/req)
HTTP/2 · TLS HTTP/2 · plain 1,676
(431 MiB / 16.4% CPU · 210 MiB/s · 294.1 µs CPU/req)
Not possible 🥇 2,428
(131 MiB / 26.7% CPU · 304 MiB/s · 329.8 µs CPU/req)
2,042
(131 MiB / 20.9% CPU · 255 MiB/s · 307.6 µs CPU/req)
1,028
(238 MiB / 17.8% CPU · 128 MiB/s · 520.0 µs CPU/req)
HTTP/2 · TLS HTTP/2 · TLS 1,895
(425 MiB / 23.7% CPU · 237 MiB/s · 375.2 µs CPU/req)
Not possible 🥇 1,915
(126 MiB / 29.1% CPU · 239 MiB/s · 456.6 µs CPU/req)
1,792
(128 MiB / 28.2% CPU · 224 MiB/s · 472.8 µs CPU/req)
889
(229 MiB / 19.3% CPU · 111 MiB/s · 650.5 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 737
(345 MiB / 28.2% CPU · 92 MiB/s · 1148.0 µs CPU/req)
Not possible 553
(121 MiB / 31.6% CPU · 69 MiB/s · 1714.4 µs CPU/req)
677
(136 MiB / 46.3% CPU · 85 MiB/s · 2051.8 µs CPU/req)
566
(417 MiB / 30.3% CPU · 71 MiB/s · 1607.6 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · TLS 835
(269 MiB / 31.4% CPU · 104 MiB/s · 1128.8 µs CPU/req)
1,084
(101 MiB / 21.0% CPU · 135 MiB/s · 582.2 µs CPU/req)
🥇 1,196
(127 MiB / 33.9% CPU · 149 MiB/s · 849.7 µs CPU/req)
492
(132 MiB / 29.5% CPU · 62 MiB/s · 1799.5 µs CPU/req)
744
(407 MiB / 30.8% CPU · 93 MiB/s · 1243.9 µs CPU/req)

Windows — lossy / high-RTT (H2 HOL / H3 loss)

Userspace 5 ms one-way delay + 1% TCP connection stall (H1/H2); UDP is loss% only (no per-datagram delay; MsQuic-safe) + 1% datagram drop (H3); 64 KiB GET. Median of 3 repeats on windows-latest @ 88187363 — 38550307008 (compare-lossy).

Not possible: HAProxy and Envoy columns are omitted (no official Windows port).

Client Origin TWP nginx YARP
HTTP/1 · TLS HTTP/1 · plain 661
(91 MiB / 4.2% CPU · 41 MiB/s · 256.5 µs CPU/req)
648
(142 MiB / 18.6% CPU · 41 MiB/s · 1144.7 µs CPU/req)
🥇 662
(120 MiB / 4.9% CPU · 41 MiB/s · 295.3 µs CPU/req)
HTTP/2 · TLS HTTP/1 · plain 🥇 248
(154 MiB / 2.8% CPU · 15 MiB/s · 449.1 µs CPU/req)
244
(144 MiB / 8.0% CPU · 15 MiB/s · 1314.2 µs CPU/req)
226
(117 MiB / 3.5% CPU · 14 MiB/s · 613.3 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · plain 849
(123 MiB / 19.0% CPU · 53 MiB/s · 893.9 µs CPU/req)
Not possible (no QUIC) 🥇 890
(175 MiB / 30.6% CPU · 56 MiB/s · 1376.8 µs CPU/req)
HTTP/2 · plain HTTP/1 · plain 248
(143 MiB / 2.0% CPU · 15 MiB/s · 321.2 µs CPU/req)
🥇 249
(127 MiB / 1.8% CPU · 16 MiB/s · 290.4 µs CPU/req)
235
(101 MiB / 2.7% CPU · 15 MiB/s · 458.8 µs CPU/req)
HTTP/2 · TLS HTTP/2 · plain 🥇 234
(125 MiB / 1.7% CPU · 15 MiB/s · 296.9 µs CPU/req)
Not possible 229
(116 MiB / 2.6% CPU · 14 MiB/s · 463.3 µs CPU/req)
HTTP/2 · TLS HTTP/2 · TLS 221
(128 MiB / 3.4% CPU · 14 MiB/s · 614.1 µs CPU/req)
Not possible 🥇 228
(120 MiB / 3.6% CPU · 14 MiB/s · 629.8 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · TLS 1,110
(121 MiB / 14.5% CPU · 69 MiB/s · 521.5 µs CPU/req)
Not possible 🥇 1,370
(194 MiB / 26.2% CPU · 86 MiB/s · 764.6 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · TLS 968
(128 MiB / 21.5% CPU · 61 MiB/s · 887.4 µs CPU/req)
Not possible (no QUIC) 🥇 1,051
(188 MiB / 31.9% CPU · 66 MiB/s · 1212.7 µs CPU/req)

H1, H2, and H3 loss sustain are in the table. Prefer the ratio columns.

Linux — lossy / high-RTT (H2 HOL / H3 loss)

Median of 3 repeats @ 88187363. Source: 38550307008 (compare-lossy; lossy H3 uses quic-http3, UDP drop-only).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 1,201
(122 MiB / 13.2% CPU · 75 MiB/s · 438.4 µs CPU/req)
🥇 1,209
(99 MiB / 12.3% CPU · 76 MiB/s · 407.8 µs CPU/req)
1,208
(84 MiB / 6.7% CPU · 76 MiB/s · 221.5 µs CPU/req)
1,198
(128 MiB / 9.2% CPU · 75 MiB/s · 306.1 µs CPU/req)
1,198
(145 MiB / 16.4% CPU · 75 MiB/s · 549.4 µs CPU/req)
HTTP/2 · TLS HTTP/1 · plain 🥇 583
(193 MiB / 11.7% CPU · 36 MiB/s · 799.5 µs CPU/req)
518
(100 MiB / 5.0% CPU · 32 MiB/s · 388.7 µs CPU/req)
528
(85 MiB / 4.0% CPU · 33 MiB/s · 301.0 µs CPU/req)
505
(130 MiB / 4.6% CPU · 32 MiB/s · 362.5 µs CPU/req)
454
(140 MiB / 12.0% CPU · 28 MiB/s · 1058.2 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · plain 🥇 1,147
(165 MiB / 24.3% CPU · 72 MiB/s · 847.7 µs CPU/req)
1,079
(113 MiB / 23.9% CPU · 67 MiB/s · 884.5 µs CPU/req)
956
(103 MiB / 18.7% CPU · 60 MiB/s · 783.9 µs CPU/req)
1,020
(138 MiB / 25.4% CPU · 64 MiB/s · 996.1 µs CPU/req)
1,123
(224 MiB / 31.3% CPU · 70 MiB/s · 1114.4 µs CPU/req)
HTTP/2 · plain HTTP/1 · plain 🥇 614
(194 MiB / 8.1% CPU · 38 MiB/s · 528.3 µs CPU/req)
555
(78 MiB / 2.6% CPU · 35 MiB/s · 188.1 µs CPU/req)
539
(72 MiB / 1.9% CPU · 34 MiB/s · 138.0 µs CPU/req)
528
(122 MiB / 2.5% CPU · 33 MiB/s · 188.1 µs CPU/req)
467
(135 MiB / 7.1% CPU · 29 MiB/s · 607.3 µs CPU/req)
HTTP/2 · TLS HTTP/2 · plain 390
(153 MiB / 5.7% CPU · 24 MiB/s · 581.8 µs CPU/req)
Not possible 🥇 550
(88 MiB / 2.9% CPU · 34 MiB/s · 208.0 µs CPU/req)
505
(129 MiB / 3.7% CPU · 32 MiB/s · 294.8 µs CPU/req)
418
(147 MiB / 8.7% CPU · 26 MiB/s · 827.9 µs CPU/req)
HTTP/2 · TLS HTTP/2 · TLS 167
(158 MiB / 5.1% CPU · 10 MiB/s · 1215.8 µs CPU/req)
Not possible 🥇 525
(87 MiB / 5.3% CPU · 33 MiB/s · 406.6 µs CPU/req)
502
(131 MiB / 6.1% CPU · 31 MiB/s · 484.1 µs CPU/req)
472
(146 MiB / 14.6% CPU · 29 MiB/s · 1237.2 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · TLS 1,542
(168 MiB / 19.8% CPU · 96 MiB/s · 513.0 µs CPU/req)
Not possible 1,504
(104 MiB / 19.1% CPU · 94 MiB/s · 509.4 µs CPU/req)
🥇 1,699
(141 MiB / 25.3% CPU · 106 MiB/s · 595.2 µs CPU/req)
1,656
(239 MiB / 29.2% CPU · 104 MiB/s · 706.4 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · TLS 1,395
(178 MiB / 22.6% CPU · 87 MiB/s · 648.2 µs CPU/req)
🥇 1,441
(121 MiB / 23.5% CPU · 90 MiB/s · 653.2 µs CPU/req)
1,311
(105 MiB / 20.1% CPU · 82 MiB/s · 612.2 µs CPU/req)
1,382
(138 MiB / 25.6% CPU · 86 MiB/s · 740.5 µs CPU/req)
1,410
(224 MiB / 30.9% CPU · 88 MiB/s · 878.3 µs CPU/req)

TWP H2 HOL ≫ YARP (~7.8×). H3 TWP÷YARP ≈ 1.07×.

macOS — lossy / high-RTT (H2 HOL / H3 loss)

Median of 3 repeats on macos-15 (Apple Silicon M1, 3-core / 7 GB) @ 88187363. Source: 38550307008 (compare-lossy; lossy H3 uses quic-http3, UDP drop-only).

Client Origin TWP nginx HAProxy Envoy YARP
HTTP/1 · TLS HTTP/1 · plain 364
(257 MiB / 3.6% CPU · 23 MiB/s · 301.2 µs CPU/req)
🥇 440
(85 MiB / 1.6% CPU · 28 MiB/s · 110.4 µs CPU/req)
383
(117 MiB / 1.9% CPU · 24 MiB/s · 145.6 µs CPU/req)
404
(130 MiB / 2.3% CPU · 25 MiB/s · 173.8 µs CPU/req)
374
(221 MiB / 3.7% CPU · 23 MiB/s · 297.2 µs CPU/req)
HTTP/2 · TLS HTTP/1 · plain 🥇 160
(438 MiB / 4.8% CPU · 10.0 MiB/s · 895.3 µs CPU/req)
136
(85 MiB / 0.8% CPU · 8.5 MiB/s · 176.9 µs CPU/req)
144
(114 MiB / 1.8% CPU · 9.0 MiB/s · 384.4 µs CPU/req)
136
(129 MiB / 1.9% CPU · 8.5 MiB/s · 411.2 µs CPU/req)
143
(224 MiB / 3.6% CPU · 9.0 MiB/s · 763.6 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · plain 933
(271 MiB / 21.7% CPU · 58 MiB/s · 697.4 µs CPU/req)
🥇 1,332
(99 MiB / 14.8% CPU · 83 MiB/s · 334.0 µs CPU/req)
634
(123 MiB / 13.9% CPU · 40 MiB/s · 658.0 µs CPU/req)
1,045
(132 MiB / 29.4% CPU · 65 MiB/s · 845.1 µs CPU/req)
732
(428 MiB / 24.6% CPU · 46 MiB/s · 1005.7 µs CPU/req)
HTTP/2 · plain HTTP/1 · plain 137
(316 MiB / 4.3% CPU · 8.6 MiB/s · 930.8 µs CPU/req)
145
(72 MiB / 0.5% CPU · 9.1 MiB/s · 109.4 µs CPU/req)
🥇 152
(102 MiB / 1.0% CPU · 9.5 MiB/s · 199.7 µs CPU/req)
136
(125 MiB / 1.4% CPU · 8.5 MiB/s · 300.4 µs CPU/req)
145
(145 MiB / 3.4% CPU · 9.1 MiB/s · 696.8 µs CPU/req)
HTTP/2 · TLS HTTP/2 · plain 128
(315 MiB / 3.4% CPU · 8.0 MiB/s · 790.0 µs CPU/req)
Not possible 🥇 144
(120 MiB / 1.4% CPU · 9.0 MiB/s · 287.7 µs CPU/req)
136
(124 MiB / 1.7% CPU · 8.5 MiB/s · 367.5 µs CPU/req)
120
(277 MiB / 3.2% CPU · 7.5 MiB/s · 806.3 µs CPU/req)
HTTP/2 · TLS HTTP/2 · TLS 116
(307 MiB / 3.2% CPU · 7.2 MiB/s · 820.8 µs CPU/req)
Not possible 🥇 138
(117 MiB / 2.0% CPU · 8.6 MiB/s · 435.1 µs CPU/req)
120
(124 MiB / 2.0% CPU · 7.5 MiB/s · 513.8 µs CPU/req)
136
(282 MiB / 4.2% CPU · 8.5 MiB/s · 924.1 µs CPU/req)
HTTP/3 · QUIC HTTP/2 · TLS 762
(180 MiB / 21.9% CPU · 48 MiB/s · 861.8 µs CPU/req)
Not possible 544
(128 MiB / 13.8% CPU · 34 MiB/s · 759.9 µs CPU/req)
🥇 1,073
(130 MiB / 30.8% CPU · 67 MiB/s · 862.3 µs CPU/req)
445
(410 MiB / 19.7% CPU · 28 MiB/s · 1330.5 µs CPU/req)
HTTP/3 · QUIC HTTP/1 · TLS 887
(208 MiB / 18.3% CPU · 55 MiB/s · 620.1 µs CPU/req)
218
(peak 406 · 82 MiB / 3.0% CPU · 14 MiB/s · 409.3 µs CPU/req)
401
(126 MiB / 14.1% CPU · 25 MiB/s · 1057.4 µs CPU/req)
🥇 1,023
(132 MiB / 29.2% CPU · 64 MiB/s · 855.3 µs CPU/req)
727
(363 MiB / 21.4% CPU · 45 MiB/s · 882.5 µs CPU/req)

Architecture-sensitive

These runs isolate slow app readers, origin-early response, HTTP/2 duplex, and WebSocket echo. See TWP vs YARP IO model. Laptop 1-rep numbers are on Performance Local Lab.

Median of 3 repeats on matched 4 vCPU / 16 GiB runners @ 88187363 (38550317747). Slow consumer = 256 KiB GET, 16 KiB read + 8 ms sleep. Early response = 64 KiB POST, origin writes after 8 KiB. Duplex HTTP/2 = overlapping 64 KiB POST on H2 TLS↔H2 TLS. WebSocket row = H1 Upgrade echo round-trips/sec (not RFC 8441; see WebSocket RFC 8441 table below).

Lossy-link runs (slow network) are already published above; they are not a slow app reader.

Windows

Not possible: HAProxy and Envoy columns are omitted (no official Windows port).

Scenario Client Origin TWP nginx YARP
Slow consumer (256 KiB GET, throttled client read) HTTP/1 · TLS HTTP/1 · plain 🥇 248
(95 MiB / 5.3% CPU · 62 MiB/s · 860.7 µs CPU/req)
223
(144 MiB / 24.6% CPU · 56 MiB/s · 4414.2 µs CPU/req)
242
(111 MiB / 4.8% CPU · 60 MiB/s · 794.7 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 248
(137 MiB / 5.5% CPU · 62 MiB/s · 883.7 µs CPU/req)
198
(142 MiB / 24.8% CPU · 50 MiB/s · 5004.0 µs CPU/req)
🥇 248
(121 MiB / 5.6% CPU · 62 MiB/s · 900.7 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 76
(109 MiB / 45.2% CPU · 19 MiB/s · 23910.1 µs CPU/req)
Not possible (no QUIC) 🥇 118
(152 MiB / 46.4% CPU · 29 MiB/s · 15765.7 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 🥇 11,160
(97 MiB / 39.3% CPU · 1,395 MiB/s · 140.9 µs CPU/req)
643
(143 MiB / 24.8% CPU · 80 MiB/s · 1541.6 µs CPU/req)
7,477
(138 MiB / 55.2% CPU · 935 MiB/s · 295.2 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 4,333
(217 MiB / 54.1% CPU · 542 MiB/s · 499.4 µs CPU/req)
488
(147 MiB / 24.8% CPU · 61 MiB/s · 2029.9 µs CPU/req)
🥇 5,116
(139 MiB / 48.8% CPU · 639 MiB/s · 381.3 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 🥇 1,769
(155 MiB / 44.7% CPU · 221 MiB/s · 1010.7 µs CPU/req)
Not possible (no QUIC) 1,083
(209 MiB / 50.7% CPU · 135 MiB/s · 1873.2 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · plain HTTP/1 · plain 248
(131 MiB / 4.3% CPU · 62 MiB/s · 691.4 µs CPU/req)
248
(128 MiB / 9.1% CPU · 62 MiB/s · 1465.3 µs CPU/req)
🥇 248
(109 MiB / 4.5% CPU · 62 MiB/s · 725.1 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/2 · TLS 244
(160 MiB / 9.0% CPU · 61 MiB/s · 1484.4 µs CPU/req)
Not possible 🥇 248
(144 MiB / 10.3% CPU · 62 MiB/s · 1672.1 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/2 · TLS 🥇 3,416
(202 MiB / 40.6% CPU · 427 MiB/s · 475.6 µs CPU/req)
Not possible 3,191
(162 MiB / 56.2% CPU · 399 MiB/s · 704.4 µs CPU/req)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 🥇 3,548
(192 MiB / 38.9% CPU · 444 MiB/s · 438.3 µs CPU/req)
Not possible 2,948
(163 MiB / 56.2% CPU · 369 MiB/s · 763.2 µs CPU/req)
Duplex (WebSocket / H1 Upgrade) HTTP/1 · TLS HTTP/1 · plain 🥇 25,483
(99 MiB / 41.4% CPU · 65.0 µs CPU/req)
12,803
(144 MiB / 24.6% CPU · 76.7 µs CPU/req)
23,656
(89 MiB / 42.6% CPU · 72.1 µs CPU/req)

Linux

Scenario Client Origin TWP nginx HAProxy Envoy YARP
Slow consumer (256 KiB GET, throttled client read) HTTP/1 · TLS HTTP/1 · plain 474
(119 MiB / 6.1% CPU · 118 MiB/s · 517.7 µs CPU/req)
418
(100 MiB / 6.7% CPU · 105 MiB/s · 638.0 µs CPU/req)
472
(86 MiB / 3.4% CPU · 118 MiB/s · 286.5 µs CPU/req)
🥇 475
(140 MiB / 4.2% CPU · 119 MiB/s · 353.1 µs CPU/req)
402
(144 MiB / 9.7% CPU · 100 MiB/s · 964.9 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 477
(169 MiB / 14.5% CPU · 119 MiB/s · 1214.2 µs CPU/req)
475
(101 MiB / 9.7% CPU · 119 MiB/s · 817.8 µs CPU/req)
480
(85 MiB / 6.7% CPU · 120 MiB/s · 555.1 µs CPU/req)
🥇 480
(145 MiB / 6.4% CPU · 120 MiB/s · 536.4 µs CPU/req)
477
(161 MiB / 16.6% CPU · 119 MiB/s · 1392.1 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 457
(144 MiB / 49.1% CPU · 114 MiB/s · 4292.2 µs CPU/req)
465
(113 MiB / 26.1% CPU · 116 MiB/s · 2245.7 µs CPU/req)
468
(92 MiB / 13.6% CPU · 117 MiB/s · 1161.9 µs CPU/req)
🥇 470
(151 MiB / 25.3% CPU · 117 MiB/s · 2158.4 µs CPU/req)
452
(208 MiB / 51.7% CPU · 113 MiB/s · 4577.7 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 4,792
(136 MiB / 47.3% CPU · 599 MiB/s · 394.9 µs CPU/req)
3,654
(99 MiB / 50.8% CPU · 457 MiB/s · 555.6 µs CPU/req)
🥇 5,342
(89 MiB / 44.2% CPU · 668 MiB/s · 330.8 µs CPU/req)
0
(peak 2,918 · 130 MiB / 26.3% CPU)
3,176
(176 MiB / 56.2% CPU · 397 MiB/s · 707.9 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 4,368
(268 MiB / 50.9% CPU · 546 MiB/s · 466.1 µs CPU/req)
4,558
(peak 4,933 · 105 MiB / 42.6% CPU · 570 MiB/s · 373.8 µs CPU/req)
🥇 5,749
(90 MiB / 42.6% CPU · 719 MiB/s · 296.1 µs CPU/req)
0
(peak 5,981 · 140 MiB / 41.6% CPU)
4,901
(174 MiB / 49.1% CPU · 613 MiB/s · 401.1 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 🥇 3,240
(247 MiB / 46.3% CPU · 405 MiB/s · 571.3 µs CPU/req)
1,456
(117 MiB / 33.7% CPU · 182 MiB/s · 927.2 µs CPU/req)
2,768
(95 MiB / 38.3% CPU · 346 MiB/s · 552.8 µs CPU/req)
0
(peak 1 · 131 MiB / 0.2% CPU)
2,118
(272 MiB / 50.3% CPU · 265 MiB/s · 949.4 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · plain HTTP/1 · plain 480
(156 MiB / 11.1% CPU · 120 MiB/s · 924.2 µs CPU/req)
476
(78 MiB / 7.3% CPU · 119 MiB/s · 614.4 µs CPU/req)
🥇 480
(71 MiB / 4.3% CPU · 120 MiB/s · 355.0 µs CPU/req)
480
(136 MiB / 5.0% CPU · 120 MiB/s · 416.7 µs CPU/req)
476
(146 MiB / 11.1% CPU · 119 MiB/s · 928.0 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/2 · TLS 466
(214 MiB / 24.7% CPU · 117 MiB/s · 2119.2 µs CPU/req)
Not possible 472
(94 MiB / 19.2% CPU · 118 MiB/s · 1629.5 µs CPU/req)
🥇 472
(140 MiB / 18.0% CPU · 118 MiB/s · 1527.3 µs CPU/req)
467
(170 MiB / 30.2% CPU · 117 MiB/s · 2588.4 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/2 · TLS 🥇 2,719
(225 MiB / 41.3% CPU · 340 MiB/s · 608.1 µs CPU/req)
Not possible 1,975
(92 MiB / 34.7% CPU · 247 MiB/s · 702.4 µs CPU/req)
0
(peak 2,431 · 142 MiB / 36.6% CPU)
1,891
(205 MiB / 53.8% CPU · 236 MiB/s · 1137.6 µs CPU/req)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 🥇 2,790
(229 MiB / 41.1% CPU · 349 MiB/s · 588.8 µs CPU/req)
Not possible 2,056
(95 MiB / 36.0% CPU · 257 MiB/s · 700.2 µs CPU/req)
0
(peak 2,309 · 142 MiB / 34.5% CPU)
1,942
(201 MiB / 53.9% CPU · 243 MiB/s · 1110.4 µs CPU/req)
Duplex (WebSocket / H1 Upgrade) HTTP/1 · TLS HTTP/1 · plain 38,303
(129 MiB / 44.4% CPU · 46.4 µs CPU/req)
🥇 42,732
(101 MiB / 36.6% CPU · 34.3 µs CPU/req)
40,797
(82 MiB / 39.3% CPU · 38.5 µs CPU/req)
38,975
(128 MiB / 41.5% CPU · 42.6 µs CPU/req)
34,360
(126 MiB / 44.9% CPU · 52.2 µs CPU/req)

macOS

Median of 3 repeats on macos-15 (Apple Silicon M1, 3-core / 7 GB) @ 88187363. Source: Actions 38550317747 (compare-arch).

Scenario Client Origin TWP nginx HAProxy Envoy YARP
Slow consumer (256 KiB GET, throttled client read) HTTP/1 · TLS HTTP/1 · plain 98
(241 MiB / 5.0% CPU · 24 MiB/s · 1535.2 µs CPU/req)
🥇 109
(85 MiB / 0.9% CPU · 27 MiB/s · 258.0 µs CPU/req)
99
(110 MiB / 1.1% CPU · 25 MiB/s · 331.3 µs CPU/req)
84
(153 MiB / 1.2% CPU · 21 MiB/s · 441.9 µs CPU/req)
95
(221 MiB / 3.1% CPU · 24 MiB/s · 985.3 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 96
(386 MiB / 6.5% CPU · 24 MiB/s · 2057.6 µs CPU/req)
🥇 104
(85 MiB / 1.8% CPU · 26 MiB/s · 528.4 µs CPU/req)
96
(116 MiB / 2.2% CPU · 24 MiB/s · 700.0 µs CPU/req)
88
(155 MiB / 2.0% CPU · 22 MiB/s · 671.2 µs CPU/req)
88
(224 MiB / 5.0% CPU · 22 MiB/s · 1712.3 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 64
(132 MiB / 18.5% CPU · 16 MiB/s · 8722.1 µs CPU/req)
104
(94 MiB / 4.6% CPU · 26 MiB/s · 1322.4 µs CPU/req)
🥇 122
(113 MiB / 11.4% CPU · 31 MiB/s · 2803.9 µs CPU/req)
102
(149 MiB / 14.5% CPU · 26 MiB/s · 4255.6 µs CPU/req)
80
(354 MiB / 16.9% CPU · 20 MiB/s · 6291.0 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 2,837
(236 MiB / 22.3% CPU · 355 MiB/s · 235.4 µs CPU/req)
0
(peak 3,234 · 89 MiB / 20.5% CPU)
🥇 3,800
(120 MiB / 28.5% CPU · 475 MiB/s · 224.8 µs CPU/req)
0
(peak 3,445 · 131 MiB / 26.6% CPU)
2,229
(205 MiB / 36.6% CPU · 279 MiB/s · 493.0 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 2,443
(491 MiB / 30.8% CPU · 305 MiB/s · 378.6 µs CPU/req)
2,712
(99 MiB / 17.6% CPU · 339 MiB/s · 194.2 µs CPU/req)
🥇 3,276
(125 MiB / 28.6% CPU · 410 MiB/s · 261.4 µs CPU/req)
0
(peak 2,398 · 135 MiB / 23.0% CPU)
1,544
(177 MiB / 22.0% CPU · 193 MiB/s · 426.6 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 600
(281 MiB / 25.6% CPU · 75 MiB/s · 1278.4 µs CPU/req)
🥇 957
(100 MiB / 20.0% CPU · 120 MiB/s · 626.3 µs CPU/req)
452
(109 MiB / 25.8% CPU · 56 MiB/s · 1713.0 µs CPU/req)
573
(134 MiB / 38.4% CPU · 72 MiB/s · 2013.4 µs CPU/req)
348
(441 MiB / 26.1% CPU · 43 MiB/s · 2252.2 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · plain HTTP/1 · plain 🥇 104
(228 MiB / 2.4% CPU · 26 MiB/s · 701.6 µs CPU/req)
103
(73 MiB / 0.9% CPU · 26 MiB/s · 270.1 µs CPU/req)
96
(102 MiB / 0.8% CPU · 24 MiB/s · 266.2 µs CPU/req)
80
(145 MiB / 1.2% CPU · 20 MiB/s · 447.4 µs CPU/req)
96
(145 MiB / 2.4% CPU · 24 MiB/s · 742.2 µs CPU/req)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/2 · TLS 🥇 96
(476 MiB / 4.0% CPU · 24 MiB/s · 1241.9 µs CPU/req)
Not possible 96
(125 MiB / 2.8% CPU · 24 MiB/s · 863.0 µs CPU/req)
88
(131 MiB / 2.3% CPU · 22 MiB/s · 802.3 µs CPU/req)
88
(350 MiB / 4.2% CPU · 22 MiB/s · 1434.9 µs CPU/req)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/2 · TLS 🥇 2,047
(472 MiB / 25.9% CPU · 256 MiB/s · 379.9 µs CPU/req)
Not possible 1,258
(peak 1,377 · 115 MiB / 18.0% CPU · 157 MiB/s · 428.4 µs CPU/req)
0
(peak 1,604 · 133 MiB / 23.3% CPU)
1,474
(353 MiB / 28.6% CPU · 184 MiB/s · 581.7 µs CPU/req)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 🥇 1,796
(452 MiB / 22.9% CPU · 225 MiB/s · 382.8 µs CPU/req)
Not possible 1,456
(119 MiB / 21.8% CPU · 182 MiB/s · 449.9 µs CPU/req)
0
(peak 1,675 · 133 MiB / 23.7% CPU)
1,708
(350 MiB / 32.1% CPU · 214 MiB/s · 563.3 µs CPU/req)
Duplex (WebSocket / H1 Upgrade) HTTP/1 · TLS HTTP/1 · plain 12,802
(153 MiB / 14.1% CPU · 33.1 µs CPU/req)
🥇 20,969
(86 MiB / 15.3% CPU · 21.9 µs CPU/req)
14,551
(103 MiB / 11.9% CPU · 24.5 µs CPU/req)
8,266
(122 MiB / 10.3% CPU · 37.3 µs CPU/req)
13,284
(148 MiB / 19.1% CPU · 43.2 µs CPU/req)

Slow consumer is sleep-bound; H1/H2/H3 sit in the same band. Prefer the ratio columns for early-response, duplex, and WebSocket rows.

TLS termination cost (H1 TLS → cleartext origin)

Isolates keep-alive tiny GET vs new connection per request (handshake-dominated) vs keep-alive 256 KiB. Product comparison uses RPS and end-to-end latency; TWP can also capture ClientTlsTiming when TWP_RPS_CAPTURE_TLS=1 (child process) — nginx/YARP have no equivalent hook.

Windows

Median of 3 repeats on windows-latest @ 88187363. Source: Actions 38550312984. Absolute RPS on GHA swings hard; prefer TWP÷YARP.

Not possible: HAProxy and Envoy columns are omitted (no official Windows port).

Workload TWP nginx YARP
Keep-alive · tiny GET 🥇 20,020
(87 MiB / 49.9% CPU · 99.7 µs CPU/req)
8,592
(142 MiB / 24.6% CPU · 114.8 µs CPU/req)
18,086
(106 MiB / 50.7% CPU · 112.1 µs CPU/req)
New-connection · tiny GET 🥇 687
(82 MiB)
245
(141 MiB / 1.8% CPU · 294.2 µs CPU/req)
682
(110 MiB)
Keep-alive · 256 KiB GET 4,146
(133 MiB / 34.2% CPU · 1,036 MiB/s · 329.7 µs CPU/req)
313
(142 MiB / 24.6% CPU · 78 MiB/s · 3141.5 µs CPU/req)
🥇 4,347
(137 MiB / 39.7% CPU · 1,087 MiB/s · 365.3 µs CPU/req)

Linux

Median of 3 repeats @ 88187363. Source: Actions 38550312984.

Workload TWP nginx HAProxy Envoy YARP
Keep-alive · tiny GET 23,709
(108 MiB / 50.0% CPU · 84.4 µs CPU/req)
🥇 28,097
(99 MiB / 41.7% CPU · 59.4 µs CPU/req)
26,502
(82 MiB / 44.7% CPU · 67.5 µs CPU/req)
16,671
(127 MiB / 58.8% CPU · 141.1 µs CPU/req)
20,557
(135 MiB / 50.7% CPU · 98.7 µs CPU/req)
New-connection · tiny GET 970
(121 MiB)
1,006
(99 MiB / 1.0% CPU · 39.0 µs CPU/req)
946
(84 MiB / 0.3% CPU · 12.7 µs CPU/req)
🥇 1,067
(130 MiB / 0.2% CPU · 9.4 µs CPU/req)
963
(148 MiB / 0.2% CPU · 10.4 µs CPU/req)
Keep-alive · 256 KiB GET 2,710
(124 MiB / 37.0% CPU · 678 MiB/s · 546.4 µs CPU/req)
1,742
(98 MiB / 53.1% CPU · 436 MiB/s · 1219.5 µs CPU/req)
🥇 2,964
(86 MiB / 32.5% CPU · 741 MiB/s · 438.4 µs CPU/req)
2,702
(145 MiB / 34.2% CPU · 676 MiB/s · 505.9 µs CPU/req)
2,124
(162 MiB / 45.8% CPU · 531 MiB/s · 862.1 µs CPU/req)

macOS

Median of 3 repeats on macos-15 @ 88187363. Source: Actions 38550312984.

Workload TWP nginx HAProxy Envoy YARP
Keep-alive · tiny GET 5,953
(126 MiB / 11.9% CPU · 60.0 µs CPU/req)
14,246
(84 MiB / 16.1% CPU · 33.9 µs CPU/req)
9,554
(108 MiB / 10.5% CPU · 32.8 µs CPU/req)
13,560
(122 MiB / 35.2% CPU · 77.9 µs CPU/req)
🥇 14,886
(160 MiB / 22.7% CPU · 45.8 µs CPU/req)
New-connection · tiny GET 107
(108 MiB / 0.2% CPU · 44.7 µs CPU/req)
609
(86 MiB)
682
(105 MiB)
🥇 769
(132 MiB / 0.1% CPU · 2.0 µs CPU/req)
114
(171 MiB / 0.2% CPU · 50.2 µs CPU/req)
Keep-alive · 256 KiB GET 1,987
(206 MiB / 24.9% CPU · 497 MiB/s · 376.4 µs CPU/req)
2,126
(84 MiB / 18.5% CPU · 532 MiB/s · 261.5 µs CPU/req)
🥇 2,394
(118 MiB / 23.2% CPU · 599 MiB/s · 291.3 µs CPU/req)
1,909
(154 MiB / 22.2% CPU · 477 MiB/s · 348.7 µs CPU/req)
1,680
(184 MiB / 28.0% CPU · 420 MiB/s · 500.8 µs CPU/req)

Prefer TWP÷YARP in the table. Absolute RPS on GHA swings hard. New-connection is Darwin SslStream-bound for TWP and YARP (handshake p99 SLO 500 ms on macOS only — Win/Linux stay at 200 ms).

Unary gRPC (H2 TLS)

Unary Echo RPC/s @ c=64 over H2 TLS→H2 TLS for Titanium, YARP, nginx (grpc_pass), HAProxy, and Envoy (OS-possible peers only). Not folded into the architecture-sensitive tables. Median of 3 repeats @ 88187363 — 38550284913.

OS Titanium YARP nginx HAProxy Envoy
Windows 🥇 42,728
(127 MiB / 32.9% CPU · 30.8 µs CPU/req)
27,262
(124 MiB / 45.4% CPU · 66.6 µs CPU/req)
0
(144 MiB / 24.9% CPU)
Not possible Not possible
Linux 🥇 36,855
(169 MiB / 33.8% CPU · 36.7 µs CPU/req)
23,039
(162 MiB / 42.5% CPU · 73.7 µs CPU/req)
0
(110 MiB / 40.3% CPU)
18,221
(83 MiB / 43.3% CPU · 95.0 µs CPU/req)
21,370
(126 MiB / 52.9% CPU · 98.9 µs CPU/req)
macOS 🥇 10,349
(274 MiB / 11.9% CPU · 34.6 µs CPU/req)
0
(209 MiB / 19.2% CPU)
0
(83 MiB / 3.4% CPU)
9,613
(116 MiB / 26.0% CPU · 81.0 µs CPU/req)
0
(121 MiB / 20.7% CPU)

Unary gRPC (H2 TLS → h2c)

Unary Echo RPC/s @ c=64 over H2 TLS → h2c (edge TLS, cleartext H2 origin). Peers: Titanium, YARP, HAProxy, Envoy. Mode: compare-grpc (*-grpc-h2c). Median of 3 repeats @ 88187363 — 38550284913.

Not possible: nginx column omitted (no H2 upstream).

OS Titanium YARP HAProxy Envoy
Windows 🥇 48,373
(134 MiB / 30.0% CPU · 24.8 µs CPU/req)
30,510
(119 MiB / 51.2% CPU · 67.1 µs CPU/req)
Not possible Not possible
Linux 🥇 38,402
(166 MiB / 33.8% CPU · 35.2 µs CPU/req)
23,085
(163 MiB / 44.0% CPU · 76.3 µs CPU/req)
17,410
(81 MiB / 44.4% CPU · 102.1 µs CPU/req)
17,851
(126 MiB / 56.9% CPU · 127.4 µs CPU/req)
macOS 🥇 9,974
(259 MiB / 15.3% CPU · 45.9 µs CPU/req)
0
(224 MiB / 17.9% CPU)
8,307
(117 MiB / 16.8% CPU · 60.5 µs CPU/req)
0
(120 MiB / 15.4% CPU)

WebSocket (H1 TLS → H1 TLS)

WebSocket echo round-trips/sec over dual-TLS H1 (proxy_ssl style). Mode: compare-ws-h1tls (*-duplex-ws-h1tls). Median of 3 repeats @ 88187363. Source: 38550328997.

OS Titanium YARP nginx HAProxy Envoy
Windows 🥇 21,671
(101 MiB / 43.8% CPU · 80.8 µs CPU/req)
21,008
(94 MiB / 42.6% CPU · 81.2 µs CPU/req)
8,984
(143 MiB / 24.9% CPU · 110.7 µs CPU/req)
Not possible Not possible
Linux 32,846
(144 MiB / 27.1% CPU · 33.0 µs CPU/req)
30,950
(136 MiB / 29.9% CPU · 38.6 µs CPU/req)
🥇 44,021
(104 MiB / 23.1% CPU · 21.0 µs CPU/req)
34,164
(86 MiB / 23.2% CPU · 27.2 µs CPU/req)
37,939
(128 MiB / 24.4% CPU · 25.8 µs CPU/req)
macOS 5,752
(158 MiB / 12.8% CPU · 66.9 µs CPU/req)
6,926
(152 MiB / 15.1% CPU · 65.5 µs CPU/req)
10,042
(94 MiB / 12.9% CPU · 38.4 µs CPU/req)
🥇 18,931
(106 MiB / 21.6% CPU · 34.3 µs CPU/req)
10,632
(119 MiB / 21.3% CPU · 60.2 µs CPU/req)

WebSocket (H2 TLS 8441 → H1)

WebSocket echo over RFC 8441 extended CONNECT (H2 TLS client → H1 plain origin). Mode: compare-ws-h2 (*-duplex-ws-h2).

The probe client sets NoDelay and writes each HTTP/2 frame as one TLS record. Median of 3 repeats @ 88187363. Source: 38550335474.

Not possible: nginx column omitted (no RFC 8441 extended CONNECT).

OS Titanium YARP HAProxy Envoy
Windows 🥇 47,603
(169 MiB / 32.5% CPU · 27.3 µs CPU/req)
0
(178 MiB / 14.4% CPU)
Not possible Not possible
Linux 🥇 41,546
(200 MiB / 49.4% CPU · 47.6 µs CPU/req)
0
(192 MiB / 42.9% CPU)
37,370
(84 MiB / 40.4% CPU · 43.3 µs CPU/req)
0
(124 MiB / 0.3% CPU)
macOS 6,345
(242 MiB / 16.4% CPU · 77.5 µs CPU/req)
0
(177 MiB / 11.4% CPU)
🥇 9,226
(109 MiB / 18.2% CPU · 59.1 µs CPU/req)
0
(116 MiB / 0.5% CPU)

Other measurements

What Result
HTTPS TTFB vs direct (median, 14 hosts) Cold ≈ parity (−1 ms); warm −25 ms (proxy faster)
HTTP/1 loopback GET (no body intercept) ~128 µs, ~9.9 KB allocated / request
Basic example footprint (Release, after load) ~74 MB working set · ~24–29 MB private bytes
dotnet run -c Release --project benchmarks/Titanium.Web.Proxy.Benchmarks -- --filter '*Throughput*'

Local laptop BDN @ 9d7c2966 (Release):

Benchmark Setup Mean Allocated / op
HTTP/1 GET through proxy Passthrough 128 µs 9.9 KB
HTTP/2 multiplexed GETs 10 concurrent streams 253 µs / batch ~4.4 KB / request

Raising limits on large hosts

There is no artificial upper clamp on server defaults. Per-endpoint overrides:

Knob Scope Default Override
ProxyServer.MaxCachedConnections process, per upstream host 128 any ≥ 1
ProxyEndPoint.MaxCachedConnections endpoint → pool depth for that EP’s sessions null (use server) e.g. 256 on reverse EP
ProxyEndPoint.MaxConcurrentClients endpoint admission null (off) any ≥ 1
ResourceLimits.MaxConcurrentStreamsPerConnection H2 streams 256 ProxyResourceLimits.Create(...)
TransparentQuicProxyEndPoint.MaxInboundBidirectionalStreams H3 100 (probe uses 256) property on EP
ForwardCleartext transparent TLS terminate false true + decrypt
proxy.MaxCachedConnections = 512;
proxy.ResourceLimits = ProxyResourceLimits.Create(
    /* … */,
    maxConcurrentStreamsPerConnection: 1000,
    maxCachedConnectionsPerHost: 512,
    /* … */);

var ep = new TransparentProxyEndPoint(IPAddress.Any, 443, decryptSsl: true)
{
    ForwardHost = "127.0.0.1",
    ForwardPort = 8080,
    ForwardCleartext = true,
    MaxCachedConnections = 256,
    GenericCertificateName = "example.com"
};
ep.BeforeSslAuthenticate += (_, a) =>
{
    a.UpstreamHttpProtocol = UpstreamHttpProtocol.Http11;
    a.AllowHttpProtocolTranslation = true; // HTTP/2 client → HTTP/1 origin
    return Task.CompletedTask;
};

Maintainer notes

CI cadence, shards, paste scripts, and gate floors for people refreshing these tables. Consumers can skip this section.

Tiered cadence

Tier Mode When
Daily / per-PR compare-spot minutes
Milestone compare-terminate / compare-matrix ~1–2h
Editions compare-editions Linux only; one job per wiki row (feature arm + its baseline)
Pre-wiki smoke compare-product-smoke (Linux, repeats=1) ~30–60 min; required before full product
Release / wiki compare-product reverse via RPS suite one job per wiki row on Win/Linux/macOS; MITM arms (twp-mitm-*) on Linux only, same job as that row's reverse peers
Unary gRPC compare-grpc one job per row (H2 TLS and h2c) on Win/Linux/macOS
WebSocket dual-TLS compare-ws-h1tls one row, three OS
WebSocket RFC 8441 compare-ws-h2 one row, three OS (nginx N/A)
Heavier tables compare-bodies, post, lossy, arch, tls-cost, compare-saturation one suite run each, one job per row, on Win/Linux/macOS

See PERF-GATES.md.

Arm shards (comparison groups)

--arm-shard takes a comparison-group key (h1c-h1c, from --print-groups), not an i/n index, so a re-run measures the same row after a peer install flake. i/n remains for smoke modes. Paste unions rps-csv-<os>-shard-* (paste-compare-product-wiki.ps1 -RunIds …). Table “Source: Actions” may list several run URLs for one table — do not mix SHAs. A single-row leg allows 60 minutes of ramp and 75 minutes overall. Free GitHub accounts run 20 jobs at once, 5 of them macOS. Give a full suite a quiet window: do not push pull requests or start other macOS jobs while it runs. Re-run infrastructure failures only (gh run rerun --failed). A failed gate is a finding and is never re-rolled; publish the last attempt, never the best of several. The RPS tables on this page are @ 88187363: product 38550284913, saturation 38550341872, bodies 38550291997, POST 38550300822, lossy 38550307008, arch 38550317747, TLS cost 38550312984, gRPC 38550322348, WebSocket H1 TLS 38550328997, WebSocket H2 38550335474, editions 38550348130. Suite aggregates for product, arch, and lossy failed post-ramp gates on a cluster of legs: Linux lossy H2 TLS→H2 TLS was about 0.35× three peers that agreed with each other; Windows H3→H2 TLS slow-consumer was 0.27× YARP; macOS had four reverse wires at or under 0.50× YARP (H2 TLS→h2c 0.36, H3→h2c 0.37, H2 TLS→H3 0.47, H1 plain→H3 0.50), and on H3→h2c the peers rose while Titanium fell versus 347a9a1b. Published cells stay that last attempt. Gate floors are unchanged. A same-VM Linux A/B of 347a9a1b against 88187363 (5 pairs, c=64) found no CI-significant Titanium change: lossy H2 TLS −15% (CI −39% to +9%) 38582510672, H3→h2c −1% 38582542878, H2 TLS→h2c +3% 38582555901, H3 slow-consumer 0% (CI ±5%) 38582567082. Same-job re-measures cleared the 0.40 floor on both SHAs: macOS H3→h2c 1.83× / 1.12× YARP (38582888190, 38582898066); macOS H2 TLS→h2c 3.24× / 4.51× (38582907076, 38582928356); Windows slow-consumer 0.98× / 0.80× (38582936158, 38582945932). The full-suite cells stay the last attempt. Reverse product rows, bodies, POST, lossy, arch, TLS cost, gRPC, both WebSocket modes, and saturation run on Windows, Linux, and macOS. TWP MITM arms run on Linux only. compare-editions (CLI and Plus) runs on Linux only. compare-spot on pull requests to beta and stable stays on all three OS. The historical v2 MITM note below the product tables stays @ 41f4adee because those run ids are from that fix, not this re-measure.

One wiki row = one GHA job’s Client×Origin cell set: TWP + YARP + nginx + HAProxy + Envoy (when the OS can run them) stay on the same VM. On Linux that job also runs TWP Lite and Full, so Lite÷Reverse stays a same-job ratio. Windows and macOS product jobs omit twp-mitm-*. Shards split rows, not individual proxies — so TWP÷YARP remains a same-job ratio. Do not compare absolute RPS across shards (different VMs).

Gate thresholds

  • Reverse product signal: TWP ÷ closest peer ≥ 0.50 (the peer among YARP, nginx, HAProxy, and Envoy whose sustain is nearest). Edition CLI/Plus ratios floor at 0.50.
  • MITM overhead: Lite÷Reverse ≥ 0.25 and Full÷Reverse ≥ 0.25 (median of 3 GHA runs @ c=64). Absolute RPS moves with runner heat; ratios are the claim.
  • Do not enable the HTTP/2 MITM multi-origin relay pool (MaxOriginHttp2ConnectionsPerAuthority > 1 under interception): Tip A/B showed Lite err%~29 and RSS blow-up, which fails the Lite/Full÷Reverse floors. Multi-origin remains gate-off compressed-relay only.
  • Editions: see PERF-GATES.md and validate-edition-gates.ps1.

Paste / regenerate

Product refresh and heavier tables:

pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-product
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-editions
pwsh tools/RpsLoadProbe/validate-edition-gates.ps1 -CsvPath tools/RpsLoadProbe/results/rps-ramp-*.csv
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-bodies
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-post
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-lossy
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-tls-cost
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-arch
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-saturation

Harness details: RpsLoadProbe. The harness does not disable TWP safety that peers also skip, and it does not retune to pass gates (PERF-GATES.md). MITM leaf keys are a shared RSA/ECDSA pair per CertificateManager (intentional RPS tradeoff).

Clone this wiki locally