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

Performance

Why this comparison is fair

These tables are a same-harness, same-origin, same-runner-class reverse-proxy comparison — not a blog-post bake-off of mismatched labs.

  • Same load generator (dotnet-httpclient), same origin process, same warmup/measure (2s / 8s), same concurrency ramp (8, 16, 32, 64), median of 3 GitHub Actions repeats.
  • Every reverse arm is three OS processes (load generator + origin child + proxy child). Origin-direct omits the proxy. Peers are never in-process with the client.
  • Same runner class per table: windows-latest / ubuntu-latest / macos-15-intel (4-core-class). Laptop High-perf and macos-latest are never mixed into these tables.
  • YARP is Yarp.ReverseProxy 2.3.0 with equivalent TLS/ALPN. nginx uses streaming knobs (keepalive 256, buffering off). HAProxy (GPL v2 Community) and Envoy (Apache 2.0) are open-source terminate peers on the same loopback shape — Linux/macOS only; Windows cells are Not possible (no official HAProxy port; Envoy Windows support discontinued). Fair knobs: HAProxy http-reuse aggressive, maxconn 256, nbthread=CPU; Envoy concurrency=CPU, cluster limits ~256, no access log, upstream TLS verify off for loopback CA. Linux nginx (mainline + http_v3) is authoritative; Windows nginx has no QUIC — those cells are Not possible, not losses.
  • MITM is TWP-only (CONNECT + forged certs). nginx/YARP cannot MITM; Lite/Full tables are overhead vs TWP reverse, not vs peers. There are no MITM charts.
  • The product signal is TWP÷YARP (gated ≥ 0.75 reverse) and MITM÷Reverse (Lite ≥ 0.50, Full ≥ 0.50), not absolute RPS. nginx / HAProxy / Envoy are wiki and chart peers only — no CI gate. Absolute RPS moves with runner heat; ratios are the claim.
  • Product fast paths (session-lite, skip-poll, compressed relay) are in-tree defaults for interception-off reverse — the same class of work YARP/nginx do. 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 (one key for all forged leaves). That is an intentional RPS tradeoff: compromise of that material affects every host minted by that manager. Reverse/YARP/nginx paths do not mint leaves.

Numbers below are Release measurements with RpsLoadProbe. Native terminate peers: nginx, HAProxy, Envoy (where the OS supports them).

For pooling knobs and certificate first-visit tuning, see Performance and pooling. For the local cool A/B lab, laptop tables, and profiling notes, see Performance Profiling.

Contents

Measurement environment

All three OS use the 4-core-class public-repo GitHub-hosted runners: Windows / Linux at 4 vCPU / 16 GiB / 14 GB SSD, macOS at macos-15-intel 4-core / 14 GB (not macos-latest). Same harness knobs (workflow_dispatch RPS saturation: warmup 2s / measure 8s; concurrency 8, 16, 32, 64; median of 3 repeats; --stop-on-slo-fail default on). Every --ramp arm is three OS processes (parent load generator + origin child + proxy child), except origin-direct arms (load gen + origin only). Prefer TWP÷YARP / TWP÷nginx ratios over absolute RPS.

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

Tiered cadence

Tier Mode When
Daily / per-PR compare-spot minutes
Milestone compare-terminate / compare-matrix ~1–2h
Editions compare-editions ~60 min (CLI / Plus / Intercept stress arms)
Cross-version (Gate 2) compare-cross-version ~1–2h vs committed 6.0 baselines
Release / wiki compare-product ~3–4h
Heavier tables compare-bodies / post / lossy / arch / bridges / tls-cost dispatch independently

See PERF-GATES.md.

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-intel)

OS macOS 15 (GitHub-hosted macos-15-intel, Intel x86_64)
CPU 4 logical processors
RAM 14 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-amd64 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

Do not use macos-latest (Apple Silicon, 3-core / 7 GB) for publishable saturation numbers.

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 @ 803a69bc — 34394853191. 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).

pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-saturation

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 Sustain Peak % of origin-HttpClient
origin-direct dotnet-httpclient 63,571
(55 MiB / 42.1% CPU)
63,571
(55 MiB / 42.1% CPU)
100.0%
origin-direct-bombardier bombardier 48,794
(56 MiB / 24.8% CPU)
48,794
(56 MiB / 24.8% CPU)
76.8%
bare-reverse-http1 dotnet-httpclient 31,458
(57 MiB / 45.7% CPU)
31,458
(57 MiB / 45.7% CPU)
49.5%
nginx-reverse-http1 dotnet-httpclient 19,660
(125 MiB / 24.9% CPU)
19,660
(125 MiB / 24.9% CPU)
30.9%
yarp-reverse-http1 dotnet-httpclient 27,402
(91 MiB / 49.8% CPU)
27,402
(91 MiB / 49.8% CPU)
43.1%
twp-reverse-http1 dotnet-httpclient 🥇 31,891
(73 MiB / 48.5% CPU)
31,891
(73 MiB / 48.5% CPU)
50.2%

Linux (ubuntu-latest)

Arm Generator Sustain Peak % of origin-HttpClient
origin-direct dotnet-httpclient 71,840
(80 MiB / 42.0% CPU)
71,840
(80 MiB / 42.0% CPU)
100.0%
origin-direct-bombardier bombardier 43,758
(80 MiB / 35.4% CPU)
43,758
(80 MiB / 35.4% CPU)
60.9%
bare-reverse-http1 dotnet-httpclient 33,382
(70 MiB / 45.0% CPU)
33,382
(70 MiB / 45.0% CPU)
46.5%
nginx-reverse-http1 dotnet-httpclient 🥇 39,834
(75 MiB / 40.7% CPU)
39,834
(75 MiB / 40.7% CPU)
55.4%
yarp-reverse-http1 dotnet-httpclient 28,584
(115 MiB / 50.6% CPU)
28,584
(115 MiB / 50.6% CPU)
39.8%
twp-reverse-http1 dotnet-httpclient 33,681
(89 MiB / 50.7% CPU)
33,681
(89 MiB / 50.7% CPU)
46.9%

Reverse peers are about 50–46% of the origin-direct HttpClient peak on this runner class (Win TWP 50.0%, Lin TWP 45.6%). 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 Sustain Peak ÷YARP ÷nginx
nginx-reverse-http2 dotnet-httpclient 11,563
(141 MiB / 24.3% CPU)
11,563
(141 MiB / 24.3% CPU)
0.33× 1.00×
yarp-reverse-http2 dotnet-httpclient 35,473
(91 MiB / 53.0% CPU)
35,473
(91 MiB / 53.0% CPU)
1.00× 3.07×
twp-reverse-http2-cleartext dotnet-httpclient 🥇 40,219
(105 MiB / 52.9% CPU)
40,219
(105 MiB / 52.9% CPU)
1.13× 3.48×

Linux (ubuntu-latest)

Arm Generator Sustain Peak ÷YARP ÷nginx
nginx-reverse-http2 dotnet-httpclient 15,477
(100 MiB / 19.4% CPU)
15,477
(100 MiB / 19.4% CPU)
0.52× 1.00×
yarp-reverse-http2 dotnet-httpclient 29,740
(122 MiB / 50.1% CPU)
29,740
(122 MiB / 50.1% CPU)
1.00× 1.92×
twp-reverse-http2-cleartext dotnet-httpclient 🥇 34,805
(115 MiB / 53.0% CPU)
34,805
(115 MiB / 53.0% CPU)
1.17× 2.25×

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.

Windows (windows-latest)

Arm Generator Sustain Peak ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient Not possible (no QUIC) Not possible (no QUIC) — —
yarp-reverse-http3-cleartext dotnet-httpclient 17,560
(159 MiB / 48.2% CPU)
17,560
(159 MiB / 48.2% CPU)
1.00× —
twp-reverse-http3-cleartext dotnet-httpclient 🥇 19,686
(108 MiB / 43.9% CPU)
19,686
(108 MiB / 43.9% CPU)
1.12× —

Linux (ubuntu-latest)

Arm Generator Sustain Peak ÷YARP ÷nginx
nginx-reverse-http3-cleartext dotnet-httpclient 0
(106 MiB / 22.7% CPU)
15,453
(106 MiB / 22.7% CPU)
0.81× 1.00×
yarp-reverse-http3-cleartext dotnet-httpclient 19,089
(183 MiB / 50.6% CPU)
19,089
(183 MiB / 50.6% CPU)
1.00× 1.24×
twp-reverse-http3-cleartext dotnet-httpclient 🥇 21,267
(146 MiB / 50.8% CPU)
21,267
(146 MiB / 50.8% CPU)
1.11× 1.38×

How to read the tables

  • Reverse = bare transparent fixed-forward (no TWP plugins / interception). nginx knobs match TWP/YARP streaming (keepalive 256, proxy_buffering off). MITM = TWP-only table on the same Client×Origin wires: Lite = no-op handlers (unchanged-lite finish reuses reverse compressed relay); Full = mutating handlers that append up to four unique headers per direction (RPS harness adds one; product uses MitmCompressedRelayHelper — no probe name in library code). Remove/replace/non-unique header growth and body mutation still force full decode/re-encode. nginx/YARP cannot MITM. HTTP/3 has no cleartext client (QUIC always encrypted).
  • Sustainable = last concurrency that still met error/latency SLOs. Peak = highest RPS in that ramp.
  • 🥇 = best among TWP / nginx / YARP on Reverse rows (or saturation blocks): highest RPS; on an RPS tie, lower Memory (RSS) then lower CPU%. MITM is TWP-only. Lite÷Reverse / Full÷Reverse = TWP MITM lite or full sustain ÷ TWP Reverse sustain on the same Client×Origin from the same compare-product job.
  • Not possible = product cannot do that path. Not measured = path exists but no published number yet for that OS.
  • Product refresh: compare-product @ 803a69bc — 34394847149 (Win/Linux) + 34400795387 (macOS). Heavier/saturation/tls:
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-product
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

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 @ 803a69bc — compare-product 34394847149. 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.

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.

Windows reverse

Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
HTTP/1 · plain HTTP/1 · plain 🥇 25054
(76 MiB / 49.4% CPU)
🥇 25054
(76 MiB / 49.4% CPU)
13771
(125 MiB / 24.8% CPU)
13771
(125 MiB / 24.8% CPU)
Not possible Not possible Not possible Not possible 21602
(85 MiB / 50.6% CPU)
21602
(85 MiB / 50.6% CPU)
HTTP/1 · plain HTTP/1 · TLS 🥇 21000
(86 MiB / 51% CPU)
🥇 21000
(86 MiB / 51% CPU)
8661
(134 MiB / 24.8% CPU)
8661
(134 MiB / 24.8% CPU)
Not possible Not possible Not possible Not possible 19518
(97 MiB / 49.8% CPU)
19518
(97 MiB / 49.8% CPU)
HTTP/1 · plain HTTP/2 · plain 🥇 36791
(111 MiB / 49.3% CPU)
🥇 36791
(111 MiB / 49.3% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 33115
(91 MiB / 49.4% CPU)
33115
(91 MiB / 49.4% CPU)
HTTP/1 · plain HTTP/2 · TLS 🥇 32151
(116 MiB / 47.1% CPU)
🥇 32151
(116 MiB / 47.1% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 29920
(96 MiB / 47% CPU)
29920
(96 MiB / 47% CPU)
HTTP/1 · plain HTTP/3 · QUIC 🥇 18702
(102 MiB / 51% CPU)
🥇 18702
(102 MiB / 51% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 18353
(116 MiB / 50.4% CPU)
18353
(116 MiB / 50.4% CPU)
HTTP/1 · TLS HTTP/1 · plain 🥇 20185
(86 MiB / 51.3% CPU)
🥇 20185
(86 MiB / 51.3% CPU)
8911
(141 MiB / 24.6% CPU)
8911
(141 MiB / 24.6% CPU)
Not possible Not possible Not possible Not possible 17946
(101 MiB / 48.8% CPU)
17946
(101 MiB / 48.8% CPU)
HTTP/1 · TLS HTTP/1 · TLS 🥇 18608
(91 MiB / 50.8% CPU)
🥇 18608
(91 MiB / 50.8% CPU)
7028
(142 MiB / 24.8% CPU)
7028
(142 MiB / 24.8% CPU)
Not possible Not possible Not possible Not possible 16843
(110 MiB / 50.9% CPU)
16843
(110 MiB / 50.9% CPU)
HTTP/1 · TLS HTTP/2 · plain 🥇 27459
(113 MiB / 46.7% CPU)
🥇 27459
(113 MiB / 46.7% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 26830
(105 MiB / 47.2% CPU)
26830
(105 MiB / 47.2% CPU)
HTTP/1 · TLS HTTP/2 · TLS 🥇 25383
(116 MiB / 46% CPU)
🥇 25383
(116 MiB / 46% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 24244
(112 MiB / 47% CPU)
24244
(112 MiB / 47% CPU)
HTTP/1 · TLS HTTP/3 · QUIC 🥇 15985
(106 MiB / 49.8% CPU)
🥇 15985
(106 MiB / 49.8% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 15343
(131 MiB / 50.2% CPU)
15343
(131 MiB / 50.2% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 35887
(93 MiB / 54.7% CPU)
🥇 35887
(93 MiB / 54.7% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 32446
(85 MiB / 52.9% CPU)
32446
(85 MiB / 52.9% CPU)
HTTP/2 · plain HTTP/1 · TLS 🥇 30762
(100 MiB / 52.7% CPU)
🥇 30762
(100 MiB / 52.7% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 27539
(97 MiB / 53.9% CPU)
27539
(97 MiB / 53.9% CPU)
HTTP/2 · plain HTTP/2 · plain 🥇 110729
(58 MiB / 28.7% CPU)
🥇 110729
(58 MiB / 28.7% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 64788
(97 MiB / 49.9% CPU)
64788
(97 MiB / 49.9% CPU)
HTTP/2 · plain HTTP/2 · TLS 🥇 88024
(66 MiB / 28.4% CPU)
🥇 88024
(66 MiB / 28.4% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 55602
(101 MiB / 46.3% CPU)
55602
(101 MiB / 46.3% CPU)
HTTP/2 · plain HTTP/3 · QUIC 🥇 31069
(121 MiB / 52.3% CPU)
🥇 31069
(121 MiB / 52.3% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 29187
(126 MiB / 53.7% CPU)
29187
(126 MiB / 53.7% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 34633
(103 MiB / 54.1% CPU)
🥇 34633
(103 MiB / 54.1% CPU)
8211
(141 MiB / 24.3% CPU)
8211
(141 MiB / 24.3% CPU)
Not possible Not possible Not possible Not possible 28909
(92 MiB / 51.8% CPU)
28909
(92 MiB / 51.8% CPU)
HTTP/2 · TLS HTTP/1 · TLS 🥇 29580
(98 MiB / 52% CPU)
🥇 29580
(98 MiB / 52% CPU)
6502
(143 MiB / 24.7% CPU)
6502
(143 MiB / 24.7% CPU)
Not possible Not possible Not possible Not possible 25330
(99 MiB / 52.8% CPU)
25330
(99 MiB / 52.8% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 103464
(78 MiB / 29.6% CPU)
🥇 103464
(78 MiB / 29.6% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 55941
(101 MiB / 51.4% CPU)
55941
(101 MiB / 51.4% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 84540
(75 MiB / 29.2% CPU)
🥇 84540
(75 MiB / 29.2% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 49628
(98 MiB / 49.7% CPU)
49628
(98 MiB / 49.7% CPU)
HTTP/2 · TLS HTTP/3 · QUIC 🥇 30994
(129 MiB / 52.8% CPU)
🥇 30994
(129 MiB / 52.8% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 26171
(125 MiB / 52.6% CPU)
26171
(125 MiB / 52.6% CPU)
HTTP/3 · QUIC HTTP/1 · plain 14596
(104 MiB / 46% CPU)
14596
(104 MiB / 46% CPU)
Not measured Not measured Not possible Not possible Not possible Not possible 🥇 14805
(141 MiB / 52.1% CPU)
🥇 14805
(141 MiB / 52.1% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 12985
(110 MiB / 45.6% CPU)
12985
(110 MiB / 45.6% CPU)
Not measured Not measured Not possible Not possible Not possible Not possible 🥇 13441
(144 MiB / 52.8% CPU)
🥇 13441
(144 MiB / 52.8% CPU)
HTTP/3 · QUIC HTTP/2 · plain 🥇 34995
(137 MiB / 48.4% CPU)
🥇 34995
(137 MiB / 48.4% CPU)
Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible Not possible Not possible Not possible 23912
(167 MiB / 47.3% CPU)
23912
(167 MiB / 47.3% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 31822
(132 MiB / 46.7% CPU)
🥇 31822
(132 MiB / 46.7% CPU)
Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible Not possible Not possible Not possible 24706
(160 MiB / 46.6% CPU)
24706
(160 MiB / 46.6% CPU)
HTTP/3 · QUIC HTTP/3 · QUIC Not measured Not measured Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 🥇 11596
(149 MiB / 53.2% CPU)
🥇 11596
(149 MiB / 53.2% CPU)

MITM (TWP only)

Same Client×Origin wires with interception on (compare-product 34394847149). 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). Completion gate: Lite ≥ 0.50× and Full ≥ 0.50× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP÷YARP ≥ 0.75× (no terminate-peer gate).

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. @ 803a69bc: 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 24644
(83 MiB / 51% CPU)
24535
(81 MiB / 53.4% CPU)
0.98× 0.98×
HTTP/1 · plain HTTP/1 · TLS 20856
(94 MiB / 56% CPU)
20127
(94 MiB / 53.2% CPU)
0.99× 0.96×
HTTP/1 · plain HTTP/2 · plain 36417
(106 MiB / 48.9% CPU)
35496
(108 MiB / 50.7% CPU)
0.99× 0.96×
HTTP/1 · plain HTTP/2 · TLS 31704
(125 MiB / 48.3% CPU)
31094
(123 MiB / 46.6% CPU)
0.99× 0.97×
HTTP/1 · plain HTTP/3 · QUIC 18089
(109 MiB / 51% CPU)
17875
(104 MiB / 51.9% CPU)
0.97× 0.96×
HTTP/1 · TLS HTTP/1 · plain 19586
(94 MiB / 51% CPU)
19437
(95 MiB / 51% CPU)
0.97× 0.96×
HTTP/1 · TLS HTTP/1 · TLS 18398
(95 MiB / 50.5% CPU)
18156
(95 MiB / 46.8% CPU)
0.99× 0.98×
HTTP/1 · TLS HTTP/2 · plain 27946
(115 MiB / 45.4% CPU)
27317
(116 MiB / 46.5% CPU)
1.02× 0.99×
HTTP/1 · TLS HTTP/2 · TLS 25320
(124 MiB / 46.4% CPU)
25183
(131 MiB / 46.8% CPU)
1× 0.99×
HTTP/1 · TLS HTTP/3 · QUIC 15601
(111 MiB / 50.4% CPU)
15558
(106 MiB / 50.2% CPU)
0.98× 0.97×
HTTP/2 · plain HTTP/1 · plain 34477
(88 MiB / 55.2% CPU)
34306
(103 MiB / 53% CPU)
0.96× 0.96×
HTTP/2 · plain HTTP/1 · TLS 27209
(100 MiB / 52.6% CPU)
29339
(104 MiB / 55.9% CPU)
0.88× 0.95×
HTTP/2 · plain HTTP/2 · plain 83369
(68 MiB / 40.1% CPU)
81696
(72 MiB / 42.7% CPU)
0.75× 0.74×
HTTP/2 · plain HTTP/2 · TLS 70034
(73 MiB / 37.5% CPU)
68939
(77 MiB / 37.9% CPU)
0.8× 0.78×
HTTP/2 · plain HTTP/3 · QUIC 27269
(114 MiB / 51.3% CPU)
30995
(126 MiB / 53.5% CPU)
0.88× 1×
HTTP/2 · TLS HTTP/1 · plain 32528
(103 MiB / 53.2% CPU)
33215
(102 MiB / 56.9% CPU)
0.94× 0.96×
HTTP/2 · TLS HTTP/1 · TLS 25532
(102 MiB / 53.5% CPU)
28655
(101 MiB / 56.9% CPU)
0.86× 0.97×
HTTP/2 · TLS HTTP/2 · plain 77426
(82 MiB / 37.9% CPU)
76384
(86 MiB / 40.8% CPU)
0.75× 0.74×
HTTP/2 · TLS HTTP/2 · TLS 67233
(82 MiB / 38.5% CPU)
66345
(85 MiB / 38% CPU)
0.8× 0.78×
HTTP/2 · TLS HTTP/3 · QUIC 26522
(125 MiB / 51.4% CPU)
29808
(121 MiB / 54.1% CPU)
0.86× 0.96×
HTTP/3 · QUIC HTTP/1 · plain 13525
(111 MiB / 45.6% CPU)
13929
(109 MiB / 47.2% CPU)
0.93× 0.95×
HTTP/3 · QUIC HTTP/1 · TLS 11708
(125 MiB / 45.1% CPU)
11802
(123 MiB / 47.6% CPU)
0.9× 0.91×
HTTP/3 · QUIC HTTP/2 · plain 23029
(132 MiB / 39.2% CPU)
30754
(138 MiB / 49.1% CPU)
0.66× 0.88×
HTTP/3 · QUIC HTTP/2 · TLS 26954
(127 MiB / 46.6% CPU)
28523
(138 MiB / 47.6% CPU)
0.85× 0.9×

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 @ 803a69bc — compare-product 34394847149. 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.

Linux reverse

Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
HTTP/1 · plain HTTP/1 · plain 32270
(89 MiB / 50.8% CPU)
32270
(89 MiB / 50.8% CPU)
🥇 38903
(75 MiB / 41.4% CPU)
🥇 38903
(75 MiB / 41.4% CPU)
37745
(64 MiB / 41.5% CPU)
37745
(64 MiB / 41.5% CPU)
20047
(115 MiB / 62.9% CPU)
20047
(115 MiB / 62.9% CPU)
28012
(116 MiB / 50% CPU)
28012
(116 MiB / 50% CPU)
HTTP/1 · plain HTTP/1 · TLS 23960
(106 MiB / 50.4% CPU)
23960
(106 MiB / 50.4% CPU)
28728
(91 MiB / 42.6% CPU)
28728
(91 MiB / 42.6% CPU)
🥇 28744
(66 MiB / 42.7% CPU)
🥇 28744
(66 MiB / 42.7% CPU)
17105
(118 MiB / 59.3% CPU)
17105
(118 MiB / 59.3% CPU)
21520
(130 MiB / 50.5% CPU)
21520
(130 MiB / 50.5% CPU)
HTTP/1 · plain HTTP/2 · plain 🥇 40144
(128 MiB / 51.6% CPU)
🥇 40144
(128 MiB / 51.6% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 35437
(124 MiB / 49% CPU)
35437
(124 MiB / 49% CPU)
HTTP/1 · plain HTTP/2 · TLS 🥇 32388
(147 MiB / 50.2% CPU)
🥇 32388
(147 MiB / 50.2% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 29872
(133 MiB / 48% CPU)
29872
(133 MiB / 48% CPU)
HTTP/1 · plain HTTP/3 · QUIC 🥇 23483
(134 MiB / 53.6% CPU)
🥇 23483
(134 MiB / 53.6% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) 21721
(153 MiB / 49.1% CPU)
21721
(153 MiB / 49.1% CPU)
HTTP/1 · TLS HTTP/1 · plain 22861
(108 MiB / 50.1% CPU)
22861
(108 MiB / 50.1% CPU)
27039
(99 MiB / 41.9% CPU)
27039
(99 MiB / 41.9% CPU)
🥇 28240
(81 MiB / 42.5% CPU)
🥇 28240
(81 MiB / 42.5% CPU)
16614
(127 MiB / 58.5% CPU)
16614
(127 MiB / 58.5% CPU)
20267
(132 MiB / 50.7% CPU)
20267
(132 MiB / 50.7% CPU)
HTTP/1 · TLS HTTP/1 · TLS 19375
(111 MiB / 49.1% CPU)
19375
(111 MiB / 49.1% CPU)
22435
(102 MiB / 41.4% CPU)
22435
(102 MiB / 41.4% CPU)
🥇 23122
(82 MiB / 42.7% CPU)
🥇 23122
(82 MiB / 42.7% CPU)
15290
(127 MiB / 56.3% CPU)
15290
(127 MiB / 56.3% CPU)
17094
(140 MiB / 50.1% CPU)
17094
(140 MiB / 50.1% CPU)
HTTP/1 · TLS HTTP/2 · plain 🥇 28474
(141 MiB / 49.7% CPU)
🥇 28474
(141 MiB / 49.7% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 25107
(150 MiB / 48.6% CPU)
25107
(150 MiB / 48.6% CPU)
HTTP/1 · TLS HTTP/2 · TLS 🥇 24122
(158 MiB / 48.2% CPU)
🥇 24122
(158 MiB / 48.2% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 22003
(146 MiB / 48.3% CPU)
22003
(146 MiB / 48.3% CPU)
HTTP/1 · TLS HTTP/3 · QUIC 🥇 17746
(146 MiB / 52% CPU)
🥇 17746
(146 MiB / 52% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) 16498
(162 MiB / 49.6% CPU)
16498
(162 MiB / 49.6% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 36472
(118 MiB / 53.7% CPU)
🥇 36472
(118 MiB / 53.7% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 34584
(115 MiB / 50.3% CPU)
34584
(115 MiB / 50.3% CPU)
HTTP/2 · plain HTTP/1 · TLS 🥇 27662
(121 MiB / 51.6% CPU)
🥇 27662
(121 MiB / 51.6% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 25603
(126 MiB / 50.7% CPU)
25603
(126 MiB / 50.7% CPU)
HTTP/2 · plain HTTP/2 · plain 🥇 85454
(91 MiB / 37.4% CPU)
🥇 85454
(91 MiB / 37.4% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 48247
(121 MiB / 47.3% CPU)
48247
(121 MiB / 47.3% CPU)
HTTP/2 · plain HTTP/2 · TLS 🥇 57924
(95 MiB / 35.4% CPU)
🥇 57924
(95 MiB / 35.4% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 39194
(130 MiB / 46% CPU)
39194
(130 MiB / 46% CPU)
HTTP/2 · plain HTTP/3 · QUIC 🥇 27971
(144 MiB / 50.6% CPU)
🥇 27971
(144 MiB / 50.6% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) 26128
(151 MiB / 47% CPU)
26128
(151 MiB / 47% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 34068
(115 MiB / 52.8% CPU)
🥇 34068
(115 MiB / 52.8% CPU)
15239
(100 MiB / 19.4% CPU)
15239
(100 MiB / 19.4% CPU)
33334
(80 MiB / 23.2% CPU)
33334
(80 MiB / 23.2% CPU)
13969
(128 MiB / 22.8% CPU)
13969
(128 MiB / 22.8% CPU)
29075
(122 MiB / 49.8% CPU)
29075
(122 MiB / 49.8% CPU)
HTTP/2 · TLS HTTP/1 · TLS 🥇 26402
(119 MiB / 50.8% CPU)
🥇 26402
(119 MiB / 50.8% CPU)
12391
(109 MiB / 19.9% CPU)
12391
(109 MiB / 19.9% CPU)
24693
(82 MiB / 24.1% CPU)
24693
(82 MiB / 24.1% CPU)
12301
(127 MiB / 23.4% CPU)
12301
(127 MiB / 23.4% CPU)
23198
(132 MiB / 50.1% CPU)
23198
(132 MiB / 50.1% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 73478
(101 MiB / 37.1% CPU)
🥇 73478
(101 MiB / 37.1% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 38506
(129 MiB / 46.7% CPU)
38506
(129 MiB / 46.7% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 54367
(104 MiB / 35.2% CPU)
🥇 54367
(104 MiB / 35.2% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 33117
(124 MiB / 45.8% CPU)
33117
(124 MiB / 45.8% CPU)
HTTP/2 · TLS HTTP/3 · QUIC 🥇 25984
(147 MiB / 50% CPU)
🥇 25984
(147 MiB / 50% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) 22404
(157 MiB / 47.3% CPU)
22404
(157 MiB / 47.3% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 20702
(144 MiB / 50.2% CPU)
🥇 20702
(144 MiB / 50.2% CPU)
0
(106 MiB / 22.5% CPU)
15098
(106 MiB / 22.5% CPU)
Not measured Not measured Not measured Not measured 18609
(181 MiB / 50.6% CPU)
18609
(181 MiB / 50.6% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 🥇 16018
(159 MiB / 48.2% CPU)
🥇 16018
(159 MiB / 48.2% CPU)
0
(115 MiB / 23% CPU)
11518
(115 MiB / 23% CPU)
Not measured Not measured Not measured Not measured 15137
(195 MiB / 51.3% CPU)
15137
(195 MiB / 51.3% CPU)
HTTP/3 · QUIC HTTP/2 · plain 🥇 28575
(156 MiB / 53.1% CPU)
🥇 28575
(156 MiB / 53.1% CPU)
Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) 23780
(194 MiB / 48.3% CPU)
23780
(194 MiB / 48.3% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 🥇 24706
(150 MiB / 50.5% CPU)
🥇 24706
(150 MiB / 50.5% CPU)
Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) 21222
(196 MiB / 47.4% CPU)
21222
(196 MiB / 47.4% CPU)
HTTP/3 · QUIC HTTP/3 · QUIC 🥇 20615
(157 MiB / 46.7% CPU)
🥇 20615
(157 MiB / 46.7% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) 16062
(197 MiB / 48.1% CPU)
16062
(197 MiB / 48.1% CPU)

MITM (TWP only)

Same Client×Origin wires with interception on (compare-product 34394847149). 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). Completion gate: Lite ≥ 0.50× and Full ≥ 0.50× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP÷YARP ≥ 0.75× (no terminate-peer gate).

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. @ 803a69bc: 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 31264
(93 MiB / 50.7% CPU)
30781
(92 MiB / 51% CPU)
0.97× 0.95×
HTTP/1 · plain HTTP/1 · TLS 23794
(112 MiB / 51.6% CPU)
23050
(114 MiB / 51.6% CPU)
0.99× 0.96×
HTTP/1 · plain HTTP/2 · plain 39350
(131 MiB / 53% CPU)
38263
(133 MiB / 53% CPU)
0.98× 0.95×
HTTP/1 · plain HTTP/2 · TLS 31906
(144 MiB / 51% CPU)
31443
(148 MiB / 50.1% CPU)
0.99× 0.97×
HTTP/1 · plain HTTP/3 · QUIC 23218
(139 MiB / 55.4% CPU)
22352
(137 MiB / 54% CPU)
0.99× 0.95×
HTTP/1 · TLS HTTP/1 · plain 22665
(114 MiB / 50.4% CPU)
22185
(116 MiB / 50.5% CPU)
0.99× 0.97×
HTTP/1 · TLS HTTP/1 · TLS 19487
(116 MiB / 50% CPU)
18935
(116 MiB / 49.7% CPU)
1.01× 0.98×
HTTP/1 · TLS HTTP/2 · plain 27599
(146 MiB / 50.5% CPU)
26821
(143 MiB / 51.8% CPU)
0.97× 0.94×
HTTP/1 · TLS HTTP/2 · TLS 23792
(168 MiB / 48.1% CPU)
22951
(168 MiB / 49.3% CPU)
0.99× 0.95×
HTTP/1 · TLS HTTP/3 · QUIC 18152
(160 MiB / 52.4% CPU)
17638
(155 MiB / 52.5% CPU)
1.02× 0.99×
HTTP/2 · plain HTTP/1 · plain 36441
(125 MiB / 54.8% CPU)
35355
(117 MiB / 54.1% CPU)
1× 0.97×
HTTP/2 · plain HTTP/1 · TLS 27060
(123 MiB / 52.5% CPU)
26132
(125 MiB / 52.3% CPU)
0.98× 0.94×
HTTP/2 · plain HTTP/2 · plain 64369
(93 MiB / 42.3% CPU)
57731
(94 MiB / 40.9% CPU)
0.75× 0.68×
HTTP/2 · plain HTTP/2 · TLS 48576
(96 MiB / 40.2% CPU)
45891
(97 MiB / 39.3% CPU)
0.84× 0.79×
HTTP/2 · plain HTTP/3 · QUIC 28238
(144 MiB / 51.1% CPU)
26735
(144 MiB / 51.1% CPU)
1.01× 0.96×
HTTP/2 · TLS HTTP/1 · plain 33103
(126 MiB / 54.3% CPU)
32632
(124 MiB / 53.8% CPU)
0.97× 0.96×
HTTP/2 · TLS HTTP/1 · TLS 25975
(123 MiB / 51.8% CPU)
24579
(124 MiB / 51.6% CPU)
0.98× 0.93×
HTTP/2 · TLS HTTP/2 · plain 56900
(110 MiB / 40.9% CPU)
52948
(111 MiB / 40.8% CPU)
0.77× 0.72×
HTTP/2 · TLS HTTP/2 · TLS 45768
(103 MiB / 38.6% CPU)
42914
(112 MiB / 39.6% CPU)
0.84× 0.79×
HTTP/2 · TLS HTTP/3 · QUIC 25991
(150 MiB / 50.7% CPU)
25507
(165 MiB / 50.4% CPU)
1× 0.98×
HTTP/3 · QUIC HTTP/1 · plain 19635
(141 MiB / 50.8% CPU)
18750
(147 MiB / 51.1% CPU)
0.95× 0.91×
HTTP/3 · QUIC HTTP/1 · TLS 15168
(170 MiB / 48.4% CPU)
14938
(159 MiB / 49.2% CPU)
0.95× 0.93×
HTTP/3 · QUIC HTTP/2 · plain 27751
(165 MiB / 55.2% CPU)
26961
(168 MiB / 55.4% CPU)
0.97× 0.94×
HTTP/3 · QUIC HTTP/2 · TLS 23431
(153 MiB / 51.9% CPU)
22946
(156 MiB / 51.5% CPU)
0.95× 0.93×
HTTP/3 · QUIC HTTP/3 · QUIC 19546
(157 MiB / 49.1% CPU)
19411
(151 MiB / 49.3% CPU)
0.95× 0.94×

macOS — Titanium vs nginx vs HAProxy vs Envoy vs YARP

Numbers are filled by tools/RpsLoadProbe/apply-wiki-paste.ps1 after compare-product on macos-15-intel (placeholder headers match the Linux table shape).

Reverse

Median of 3 repeats on macos-15-intel (4-core / 14 GB). Bare reverse 5×5 @ 803a69bc — compare-product 34400795387 (macOS retry; Win/Linux 34394847149). 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-amd64 1.36.7), Homebrew libmsquic (+ DYLD_*), and YARP. Do not publish from macos-latest (3-core / 7 GB).

macOS reverse

Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
HTTP/1 · plain HTTP/1 · plain 15153
(82 MiB / 34.5% CPU)
15153
(82 MiB / 34.5% CPU)
11466
(52 MiB / 16% CPU)
11466
(52 MiB / 16% CPU)
🥇 19500
(47 MiB / 25.2% CPU)
🥇 19500
(47 MiB / 25.2% CPU)
4395
(73 MiB / 21.6% CPU)
4395
(73 MiB / 21.6% CPU)
14271
(103 MiB / 37.7% CPU)
14271
(103 MiB / 37.7% CPU)
HTTP/1 · plain HTTP/1 · TLS 🥇 16905
(93 MiB / 37.1% CPU)
🥇 16905
(93 MiB / 37.1% CPU)
7482
(72 MiB / 17.5% CPU)
7482
(72 MiB / 17.5% CPU)
15247
(53 MiB / 28.6% CPU)
15247
(53 MiB / 28.6% CPU)
3614
(75 MiB / 21.7% CPU)
3614
(75 MiB / 21.7% CPU)
15333
(118 MiB / 39.8% CPU)
15333
(118 MiB / 39.8% CPU)
HTTP/1 · plain HTTP/2 · plain 21595
(91 MiB / 34.9% CPU)
21595
(91 MiB / 34.9% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 🥇 26267
(109 MiB / 36.2% CPU)
🥇 26267
(109 MiB / 36.2% CPU)
HTTP/1 · plain HTTP/2 · TLS 🥇 20225
(108 MiB / 36.8% CPU)
🥇 20225
(108 MiB / 36.8% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 16519
(114 MiB / 35.9% CPU)
16519
(114 MiB / 35.9% CPU)
HTTP/1 · plain HTTP/3 · QUIC 6297
(91 MiB / 46.6% CPU)
6297
(91 MiB / 46.6% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) 🥇 7119
(109 MiB / 32.9% CPU)
🥇 7119
(109 MiB / 32.9% CPU)
HTTP/1 · TLS HTTP/1 · plain 🥇 14450
(92 MiB / 34.7% CPU)
🥇 14450
(92 MiB / 34.7% CPU)
7448
(73 MiB / 14.6% CPU)
7448
(73 MiB / 14.6% CPU)
13196
(64 MiB / 28.6% CPU)
13196
(64 MiB / 28.6% CPU)
6054
(79 MiB / 36.1% CPU)
6054
(79 MiB / 36.1% CPU)
9983
(114 MiB / 34.8% CPU)
9983
(114 MiB / 34.8% CPU)
HTTP/1 · TLS HTTP/1 · TLS 🥇 12591
(97 MiB / 34.6% CPU)
🥇 12591
(97 MiB / 34.6% CPU)
8262
(81 MiB / 20.8% CPU)
8262
(81 MiB / 20.8% CPU)
11036
(66 MiB / 29.6% CPU)
11036
(66 MiB / 29.6% CPU)
3942
(81 MiB / 31.9% CPU)
3942
(81 MiB / 31.9% CPU)
10262
(122 MiB / 34.5% CPU)
10262
(122 MiB / 34.5% CPU)
HTTP/1 · TLS HTTP/2 · plain 🥇 16192
(96 MiB / 35.7% CPU)
🥇 16192
(96 MiB / 35.7% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 16148
(120 MiB / 32.1% CPU)
16148
(120 MiB / 32.1% CPU)
HTTP/1 · TLS HTTP/2 · TLS 12371
(145 MiB / 33.2% CPU)
12371
(145 MiB / 33.2% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 🥇 14753
(121 MiB / 33% CPU)
🥇 14753
(121 MiB / 33% CPU)
HTTP/1 · TLS HTTP/3 · QUIC 4458
(104 MiB / 49% CPU)
4458
(104 MiB / 49% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) 🥇 6082
(121 MiB / 34.8% CPU)
🥇 6082
(121 MiB / 34.8% CPU)
HTTP/2 · plain HTTP/1 · plain 🥇 21171
(89 MiB / 38.9% CPU)
🥇 21171
(89 MiB / 38.9% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 20417
(104 MiB / 38.5% CPU)
20417
(104 MiB / 38.5% CPU)
HTTP/2 · plain HTTP/1 · TLS 🥇 19464
(95 MiB / 41.7% CPU)
🥇 19464
(95 MiB / 41.7% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 16927
(118 MiB / 41% CPU)
16927
(118 MiB / 41% CPU)
HTTP/2 · plain HTTP/2 · plain 🥇 46942
(73 MiB / 27.6% CPU)
🥇 46942
(73 MiB / 27.6% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 33850
(108 MiB / 35.7% CPU)
33850
(108 MiB / 35.7% CPU)
HTTP/2 · plain HTTP/2 · TLS 🥇 42076
(76 MiB / 28.3% CPU)
🥇 42076
(76 MiB / 28.3% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 31738
(109 MiB / 35.8% CPU)
31738
(109 MiB / 35.8% CPU)
HTTP/2 · plain HTTP/3 · QUIC 6771
(96 MiB / 48.5% CPU)
6771
(96 MiB / 48.5% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) 🥇 7184
(116 MiB / 34.2% CPU)
🥇 7184
(116 MiB / 34.2% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 15896
(91 MiB / 39.2% CPU)
🥇 15896
(91 MiB / 39.2% CPU)
9042
(72 MiB / 12.9% CPU)
9042
(72 MiB / 12.9% CPU)
8392
(65 MiB / 17.8% CPU)
8392
(65 MiB / 17.8% CPU)
4189
(81 MiB / 20.9% CPU)
4189
(81 MiB / 20.9% CPU)
14938
(111 MiB / 39.2% CPU)
14938
(111 MiB / 39.2% CPU)
HTTP/2 · TLS HTTP/1 · TLS 🥇 17180
(96 MiB / 42% CPU)
🥇 17180
(96 MiB / 42% CPU)
9604
(92 MiB / 15.5% CPU)
9604
(92 MiB / 15.5% CPU)
7198
(66 MiB / 17.9% CPU)
7198
(66 MiB / 17.9% CPU)
4175
(82 MiB / 20.3% CPU)
4175
(82 MiB / 20.3% CPU)
15520
(121 MiB / 39.5% CPU)
15520
(121 MiB / 39.5% CPU)
HTTP/2 · TLS HTTP/2 · plain 🥇 40490
(81 MiB / 29% CPU)
🥇 40490
(81 MiB / 29% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 30100
(110 MiB / 36% CPU)
30100
(110 MiB / 36% CPU)
HTTP/2 · TLS HTTP/2 · TLS 🥇 31123
(84 MiB / 27% CPU)
🥇 31123
(84 MiB / 27% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 28290
(114 MiB / 36.2% CPU)
28290
(114 MiB / 36.2% CPU)
HTTP/2 · TLS HTTP/3 · QUIC 6440
(108 MiB / 40.5% CPU)
6440
(108 MiB / 40.5% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) 🥇 7422
(120 MiB / 36.3% CPU)
🥇 7422
(120 MiB / 36.3% CPU)
HTTP/3 · QUIC HTTP/1 · plain 5779
(101 MiB / 40.8% CPU)
5779
(101 MiB / 40.8% CPU)
0
(62 MiB / 10.9% CPU)
8227
(62 MiB / 10.9% CPU)
🥇 9880
(65 MiB / 19.6% CPU)
🥇 9880
(65 MiB / 19.6% CPU)
Not measured Not measured 7469
(189 MiB / 35.5% CPU)
7469
(189 MiB / 35.5% CPU)
HTTP/3 · QUIC HTTP/1 · TLS 4933
(115 MiB / 41.3% CPU)
4933
(115 MiB / 41.3% CPU)
0
(66 MiB / 12% CPU)
6050
(66 MiB / 12% CPU)
🥇 6739
(68 MiB / 18.2% CPU)
🥇 6739
(68 MiB / 18.2% CPU)
Not measured Not measured 6056
(185 MiB / 35.8% CPU)
6056
(185 MiB / 35.8% CPU)
HTTP/3 · QUIC HTTP/2 · plain 6173
(98 MiB / 39.2% CPU)
6173
(98 MiB / 39.2% CPU)
Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) 🥇 8531
(167 MiB / 35% CPU)
🥇 8531
(167 MiB / 35% CPU)
HTTP/3 · QUIC HTTP/2 · TLS 6677
(106 MiB / 41% CPU)
6677
(106 MiB / 41% CPU)
Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) Not possible (no H3 to H2) 🥇 8365
(177 MiB / 34.2% CPU)
🥇 8365
(177 MiB / 34.2% CPU)
HTTP/3 · QUIC HTTP/3 · QUIC 4537
(97 MiB / 35.9% CPU)
4537
(97 MiB / 35.9% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) Not possible (no QUIC) 🥇 4746
(169 MiB / 33.8% CPU)
🥇 4746
(169 MiB / 33.8% CPU)

MITM (TWP only)

Same Client×Origin wires with interception on (compare-product 34394847149). 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). Completion gate: Lite ≥ 0.50× and Full ≥ 0.50× reverse sustain @ c=64 (median of 3 GHA runs); reverse TWP÷YARP ≥ 0.75× (no terminate-peer gate).

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. @ 803a69bc: 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 16913
(82 MiB / 36% CPU)
20795
(85 MiB / 35.8% CPU)
1.12× 1.37×
HTTP/1 · plain HTTP/1 · TLS 13436
(95 MiB / 37.2% CPU)
17140
(96 MiB / 37.5% CPU)
0.79× 1.01×
HTTP/1 · plain HTTP/2 · plain 16965
(91 MiB / 39.2% CPU)
22762
(94 MiB / 37.9% CPU)
0.79× 1.05×
HTTP/1 · plain HTTP/2 · TLS 15602
(103 MiB / 38.7% CPU)
14831
(103 MiB / 36.1% CPU)
0.77× 0.73×
HTTP/1 · plain HTTP/3 · QUIC 4422
(93 MiB / 47.7% CPU)
5232
(92 MiB / 45.1% CPU)
0.7× 0.83×
HTTP/1 · TLS HTTP/1 · plain 12258
(94 MiB / 34% CPU)
13496
(96 MiB / 33.5% CPU)
0.85× 0.93×
HTTP/1 · TLS HTTP/1 · TLS 11302
(98 MiB / 34.7% CPU)
11699
(99 MiB / 35% CPU)
0.9× 0.93×
HTTP/1 · TLS HTTP/2 · plain 11398
(98 MiB / 36.2% CPU)
11835
(99 MiB / 38.1% CPU)
0.7× 0.73×
HTTP/1 · TLS HTTP/2 · TLS 9850
(153 MiB / 35.3% CPU)
11254
(143 MiB / 35.1% CPU)
0.8× 0.91×
HTTP/1 · TLS HTTP/3 · QUIC 3489
(128 MiB / 49.3% CPU)
3404
(103 MiB / 47.1% CPU)
0.78× 0.76×
HTTP/2 · plain HTTP/1 · plain 19071
(87 MiB / 34.8% CPU)
16949
(89 MiB / 39.7% CPU)
0.9× 0.8×
HTTP/2 · plain HTTP/1 · TLS 14079
(95 MiB / 41.4% CPU)
18441
(97 MiB / 40.6% CPU)
0.72× 0.95×
HTTP/2 · plain HTTP/2 · plain 36658
(78 MiB / 32.6% CPU)
40098
(76 MiB / 33.8% CPU)
0.78× 0.85×
HTTP/2 · plain HTTP/2 · TLS 26984
(81 MiB / 31.6% CPU)
38890
(83 MiB / 33% CPU)
0.64× 0.92×
HTTP/2 · plain HTTP/3 · QUIC 6433
(94 MiB / 44% CPU)
6663
(94 MiB / 44.5% CPU)
0.95× 0.98×
HTTP/2 · TLS HTTP/1 · plain 24647
(91 MiB / 38.6% CPU)
25719
(93 MiB / 40.6% CPU)
1.55× 1.62×
HTTP/2 · TLS HTTP/1 · TLS 20502
(97 MiB / 42.1% CPU)
18318
(96 MiB / 39.4% CPU)
1.19× 1.07×
HTTP/2 · TLS HTTP/2 · plain 41277
(86 MiB / 31.4% CPU)
40008
(87 MiB / 32.4% CPU)
1.02× 0.99×
HTTP/2 · TLS HTTP/2 · TLS 38592
(86 MiB / 31.3% CPU)
39114
(87 MiB / 33% CPU)
1.24× 1.26×
HTTP/2 · TLS HTTP/3 · QUIC 7387
(107 MiB / 41.2% CPU)
7135
(106 MiB / 42.9% CPU)
1.15× 1.11×
HTTP/3 · QUIC HTTP/1 · plain 6204
(102 MiB / 38.2% CPU)
4773
(103 MiB / 40.4% CPU)
1.07× 0.83×
HTTP/3 · QUIC HTTP/1 · TLS 6132
(115 MiB / 42.5% CPU)
4390
(117 MiB / 39% CPU)
1.24× 0.89×
HTTP/3 · QUIC HTTP/2 · plain 6846
(99 MiB / 40.2% CPU)
5659
(99 MiB / 40.9% CPU)
1.11× 0.92×
HTTP/3 · QUIC HTTP/2 · TLS 7116
(109 MiB / 40.2% CPU)
6209
(106 MiB / 40% CPU)
1.07× 0.93×
HTTP/3 · QUIC HTTP/3 · QUIC 4382
(95 MiB / 36.9% CPU)
3684
(95 MiB / 39.4% CPU)
0.97× 0.81×

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).

Gates prioritize SLO survival under load and reasonable overhead vs baseline. Pre-origin Plus middleware (CIDR/WAF/JWT/rate-limit) runs on H1 terminate-lite without SessionEventArgs; JWT caches successful bearer validations. Thresholds — see PERF-GATES.md.

Median of 3 repeats @ 6d2a7c9d. Source: Actions 33259699099 (compare-editions). Warmup 2s / measure 8s; concurrency 8–64; sustain = median peak RPS among SLO-pass steps @ c=64. RPS cells include (MiB / CPU%) at that step.

pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-editions
pwsh tools/RpsLoadProbe/validate-edition-gates.ps1 -CsvPath tools/RpsLoadProbe/results/rps-ramp-*.csv
Arm Win sustain Win peak Linux sustain Linux peak Win÷ Lin÷ Gate
twp-cli-reverse-http1 vs library 32214
(118 MiB / 43.4% CPU)
32219 47734
(151 MiB / 49.0% CPU)
47844 1.02× 1.04× ≥ 0.80×
twp-cli-reverse-http1-tls vs library TLS 26495
(138 MiB / 48.3% CPU)
26651 36932
(174 MiB / 48.5% CPU)
37200 1.01× 1.00× ≥ 0.80×
twp-cli-reverse-http1-route vs CLI 32394
(120 MiB / 47.7% CPU)
32428 47369
(149 MiB / 49.2% CPU)
47398 1.01× 0.99× ≥ 0.90×
twp-cli-plus-base-http1 vs CLI 32475
(123 MiB / 46.0% CPU)
32695 47322
(154 MiB / 49.8% CPU)
47744 1.01× 0.99× ≥ 0.90×
twp-cli-plus-cache-http1 (cold) vs CLI 34029
(118 MiB / 64.5% CPU)
34460 49409
(148 MiB / 62.3% CPU)
50104 1.06× 1.04× ≥ 0.70×
twp-cli-intercept-http1 vs CLI 25219
(124 MiB / 54.0% CPU)
25377 35969
(155 MiB / 51.2% CPU)
35977 0.78× 0.75× ≥ 0.70×
twp-cli-plus-waf-http1 vs CLI 31653
(124 MiB / 46.8% CPU)
31837 46367
(151 MiB / 49.6% CPU)
46685 0.98× 0.97× ≥ 0.80×
twp-cli-plus-cidr-http1 vs CLI 31730
(123 MiB / 51.1% CPU)
31757 46828
(150 MiB / 50.2% CPU)
47093 0.98× 0.98× ≥ 0.80×
twp-cli-plus-jwt-http1 vs CLI 30326
(138 MiB / 46.6% CPU)
30386 43906
(179 MiB / 49.5% CPU)
44433 0.94× 0.92× ≥ 0.70×
twp-cli-plus-ratelimit-http1 vs CLI 31713
(123 MiB / 48.8% CPU)
31944 46435
(151 MiB / 49.4% CPU)
46860 0.98× 0.97× ≥ 0.80×
twp-cli-plus-resilience-http1 vs CLI 32373
(128 MiB / 50.5% CPU)
32416 47302
(155 MiB / 49.5% CPU)
47654 1.00× 0.99× ≥ 0.85×
twp-cli-plus-discovery-file-http1 vs CLI 32209
(121 MiB / 46.5% CPU)
32332 47485
(151 MiB / 49.1% CPU)
47770 1.00× 0.99× ≥ 0.80×
twp-cli-plus-metrics-scrape-http1 vs CLI 32192
(126 MiB / 44.8% CPU)
32427 47348
(159 MiB / 49.5% CPU)
48665 1.00× 0.99× ≥ 0.80×
twp-cli-plus-cache-hit-http1 vs cache cold 34015
(117 MiB / 66.0% CPU)
34022 49153
(149 MiB / 63.1% CPU)
49358 1.00× 0.99× ≥ 0.90×
twp-cli-static-http1 vs CLI 53078
(109 MiB / 52.1% CPU)
53980 78333
(150 MiB / 48.4% CPU)
82955 1.65× 1.64× ≥ 0.85×
twp-cli-logging-http1 vs CLI 32440
(119 MiB / 47.4% CPU)
32581 47624
(150 MiB / 49.3% CPU)
48604 1.01× 1.00× ≥ 0.90×
twp-cli-lb-leasttime-http1 vs route 29649
(134 MiB / 54.7% CPU)
30386 44790
(180 MiB / 50.6% CPU)
44825 0.92× 0.95× ≥ 0.85×
twp-cli-dialect-twp-http1 vs CLI 32461
(114 MiB / 49.7% CPU)
32780 47898
(145 MiB / 49.3% CPU)
48154 1.01× 1.00× ≥ 0.90×

validate-edition-gates.ps1 passed on both OS for this run. Library baselines @ c=64 (same job): Win H1 31561 / TLS 26128; Linux H1 45818 / TLS 36869. Laptop smoke ratios stay on Performance Local Lab — Editions.

Cross-version (7.0 vs 6.0)

Gate 2 prerequisite before the v7.0.0 tag: run compare-cross-version (reverse matrix, routes unset) on develop and compare against committed 6.0 baselines from GHA 33087088466 (tools/RpsLoadProbe/results/baseline-6.0-win.csv / baseline-6.0-linux.csv).

Gate Threshold
Peer-normalized RPS (when YARP peer exists) (TWP÷YARP)_7 ÷ (TWP÷YARP)_6 ≥ 0.90 or current (TWP÷YARP) ≥ 0.90
Absolute RPS floor (TWP arms) 7.0 ÷ 6.0 ≥ 0.70 (runner heat — peers move with the box)
RSS 7.0 ÷ 6.0 ≤ 1.20 per TWP arm @ c=64

nginx/YARP absolute RPS is not gated. See validate-cross-version.ps1.

pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-cross-version
pwsh tools/RpsLoadProbe/validate-cross-version.ps1 `
  -BaselineCsv tools/RpsLoadProbe/results/baseline-6.0-win.csv `
  -CurrentCsv  tools/RpsLoadProbe/results/rps-ramp-*.csv

Median of 3 @ 0ef6d4dd. Source: Actions 33270571908. Sustain RPS @ c=64 (SLO-pass median). Absolute 7.0÷6.0 ≈ 0.96–1.00× on same-protocol arms; peer-norm ≈ 0.90–1.03×.

Arm Win 6.0 Win 7.0 Win÷ Linux 6.0 Linux 7.0 Lin÷
twp-reverse-http1 32525 32213 0.99× 32755 31932 0.97×
twp-reverse-http1-tls 26461 26570 1.00× 22784 22351 0.98×
twp-reverse-http2 76038 75316 0.99× 47690 46678 0.98×
twp-reverse-http2-cleartext 40347 40236 1.00× 34556 33316 0.96×
twp-reverse-http3 17529 17380 0.99× 19468 19423 1.00×
twp-reverse-http3-cleartext 19956 19529 0.98× 20395 20070 0.98×
yarp-reverse-http1 (peer) 27504 27254 0.99× 27713 27102 0.98×
yarp-reverse-http2 (peer) 35553 35189 0.99× 29111 28733 0.99×

validate-cross-version.ps1 passed on both OS CSVs (RSS floor 1.20; peer-norm also passes when current TWP÷YARP ≥ 0.90). MITM arms live in compare-product / compare-mitm, not this reverse-only matrix.

Heavier reverse workloads

Separate from the tiny-GET matrix. Same measurement environments. Modes: compare-bodies, compare-post, compare-lossy, compare-tls-cost, compare-arch in RpsLoadProbe. Dispatch each independently via workflow_dispatch (no need to re-run full compare-product). Charts: render-heavier-charts.py (rps-heavier-*-{windows,linux}.png). 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. compare-arch is the slow-consumer / early-response / duplex set. Laptop numbers are on Performance Local Lab; CI medians go in the tables below. Tables below are pre–5-peer publish; re-paste with paste-heavier-wiki.py after the next heavier GHA runs for HAProxy/Envoy columns.

Lossy link = userspace shim (not kernel netem): TCP gets per-buffer delay + occasional whole-connection stalls (honest HOL for multiplexed H2); UDP gets per-datagram delay + drops (QUIC). compare-lossy publishes H1/H2/H3; H3 is where the protocol design is supposed to matter.

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

Windows heavier bodies

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

Body Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
64 KiB HTTP/1 · TLS HTTP/1 · plain 🥇 8,414
(113 MiB / 45.9% CPU)
8,414
(113 MiB / 45.9% CPU)
594
(141 MiB / 24.8% CPU)
594
(141 MiB / 24.8% CPU)
Not possible Not possible Not possible Not possible 7,558
(129 MiB / 50.3% CPU)
7,558
(129 MiB / 50.3% CPU)
64 KiB HTTP/2 · TLS HTTP/1 · plain 🥇 7,926
(165 MiB / 46.6% CPU)
7,926
(165 MiB / 46.6% CPU)
572
(141 MiB / 24.8% CPU)
572
(141 MiB / 24.8% CPU)
Not possible Not possible Not possible Not possible 6,605
(133 MiB / 50.5% CPU)
6,605
(133 MiB / 50.5% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 3,966
(135 MiB / 40.1% CPU)
3,966
(135 MiB / 40.1% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 3,796
(195 MiB / 49.4% CPU)
3,796
(195 MiB / 49.4% CPU)
256 KiB HTTP/1 · TLS HTTP/1 · plain 🥇 2,766
(118 MiB / 45.0% CPU)
2,766
(118 MiB / 45.0% CPU)
0
(142 MiB / 24.4% CPU)
150
(142 MiB / 24.4% CPU)
Not possible Not possible Not possible Not possible 2,421
(135 MiB / 50.8% CPU)
2,421
(135 MiB / 50.8% CPU)
256 KiB HTTP/2 · TLS HTTP/1 · plain 🥇 2,529
(153 MiB / 41.2% CPU)
2,529
(153 MiB / 41.2% CPU)
0
(141 MiB / 24.9% CPU)
136
(141 MiB / 24.9% CPU)
Not possible Not possible Not possible Not possible 1,640
(133 MiB / 46.9% CPU)
1,640
(133 MiB / 46.9% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 1,166
(111 MiB / 38.6% CPU)
1,166
(111 MiB / 38.6% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 1,043
(166 MiB / 43.8% CPU)
1,043
(166 MiB / 43.8% CPU)

nginx/Windows collapses on large reverse bodies in this harness; treat as same-OS only. H1 TLS 64 KiB ≈ 1.11× YARP; 256 KiB ≈ 1.23×. H2→H1 64 KiB ≈ 1.21×; H3→H1 64 KiB ≈ 1.18×.

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

Linux heavier bodies

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

Body Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
64 KiB HTTP/1 · TLS HTTP/1 · plain 8,706
(176 MiB / 44.5% CPU)
8,706
(176 MiB / 44.5% CPU)
5,827
(99 MiB / 49.4% CPU)
5,827
(99 MiB / 49.4% CPU)
🥇 10,468
(81 MiB / 38.2% CPU)
10,468
(81 MiB / 38.2% CPU)
8,499
(131 MiB / 43.5% CPU)
8,499
(131 MiB / 43.5% CPU)
7,253
(171 MiB / 47.9% CPU)
7,253
(171 MiB / 47.9% CPU)
64 KiB HTTP/2 · TLS HTTP/1 · plain 6,098
(239 MiB / 40.3% CPU)
6,098
(239 MiB / 40.3% CPU)
2,095
(100 MiB / 17.0% CPU)
2,095
(100 MiB / 17.0% CPU)
🥇 6,926
(84 MiB / 24.3% CPU)
6,926
(84 MiB / 24.3% CPU)
5,335
(141 MiB / 23.3% CPU)
5,335
(141 MiB / 23.3% CPU)
5,303
(163 MiB / 46.7% CPU)
5,303
(163 MiB / 46.7% CPU)
64 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 6,060
(188 MiB / 44.0% CPU)
6,060
(188 MiB / 44.0% CPU)
1,819
(129 MiB / 24.3% CPU)
1,819
(129 MiB / 24.3% CPU)
Not measured Not measured Not measured Not measured 4,675
(226 MiB / 51.4% CPU)
4,675
(226 MiB / 51.4% CPU)
256 KiB HTTP/1 · TLS HTTP/1 · plain 2,840
(130 MiB / 35.9% CPU)
2,840
(130 MiB / 35.9% CPU)
1,852
(99 MiB / 52.3% CPU)
1,852
(99 MiB / 52.3% CPU)
🥇 3,126
(81 MiB / 31.9% CPU)
3,126
(81 MiB / 31.9% CPU)
2,804
(145 MiB / 32.4% CPU)
2,804
(145 MiB / 32.4% CPU)
2,265
(167 MiB / 44.6% CPU)
2,265
(167 MiB / 44.6% CPU)
256 KiB HTTP/2 · TLS HTTP/1 · plain 1,564
(195 MiB / 31.4% CPU)
1,564
(195 MiB / 31.4% CPU)
663
(99 MiB / 22.6% CPU)
663
(99 MiB / 22.6% CPU)
🥇 1,894
(84 MiB / 23.0% CPU)
1,894
(84 MiB / 23.0% CPU)
1,879
(161 MiB / 20.2% CPU)
1,879
(161 MiB / 20.2% CPU)
1,487
(165 MiB / 42.6% CPU)
1,487
(165 MiB / 42.6% CPU)
256 KiB HTTP/3 · QUIC HTTP/1 · plain 🥇 1,544
(155 MiB / 43.3% CPU)
1,544
(155 MiB / 43.3% CPU)
0
(120 MiB / 21.9% CPU)
127
(120 MiB / 21.9% CPU)
Not measured Not measured Not measured Not measured 1,327
(220 MiB / 47.8% CPU)
1,327
(220 MiB / 47.8% CPU)

On this GHA pass TWP÷YARP H1 TLS ≈ 1.23× (64 KiB) / 1.28× (256 KiB); H2→H1 ≈ 1.23× / 1.16×; H3→H1 ≈ 1.27× / 1.13×. TWP÷nginx H1 TLS ≈ 1.43 / 1.58. Absolute RPS swings by VM; prefer ratios.

Windows — POST 64 KiB request + 64 KiB response

Windows heavier POST

Median of 3 repeats on windows-latest @ 803a69bc. Source: Actions 34394866049 (compare-post).

Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
HTTP/1 · TLS HTTP/1 · plain 🥇 5,742
(100 MiB / 44.0% CPU)
5,742
(100 MiB / 44.0% CPU)
360
(142 MiB / 24.7% CPU)
360
(142 MiB / 24.7% CPU)
Not possible Not possible Not possible Not possible 4,046
(138 MiB / 56.9% CPU)
4,046
(138 MiB / 56.9% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 4,046
(177 MiB / 48.0% CPU)
4,046
(177 MiB / 48.0% CPU)
342
(143 MiB / 24.5% CPU)
342
(143 MiB / 24.5% CPU)
Not possible Not possible Not possible Not possible 3,452
(132 MiB / 52.2% CPU)
3,452
(132 MiB / 52.2% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 2,018
(174 MiB / 42.4% CPU)
2,018
(174 MiB / 42.4% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 1,999
(213 MiB / 49.3% CPU)
1,999
(213 MiB / 49.3% CPU)

TWP leads H1 POST (1.5× YARP), H2 POST (1.2× YARP), and H3 POST (~1.1× YARP).

Linux — POST 64 KiB request + 64 KiB response

Linux heavier POST

Median of 3 repeats @ 803a69bc. Source: Actions 34394866049 (compare-post).

Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
HTTP/1 · TLS HTTP/1 · plain 9,319
(132 MiB / 41.0% CPU)
9,319
(132 MiB / 41.0% CPU)
7,612
(102 MiB / 44.0% CPU)
7,612
(102 MiB / 44.0% CPU)
🥇 11,038
(82 MiB / 35.8% CPU)
11,038
(82 MiB / 35.8% CPU)
9,568
(130 MiB / 41.8% CPU)
9,568
(130 MiB / 41.8% CPU)
6,084
(177 MiB / 53.6% CPU)
6,084
(177 MiB / 53.6% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 5,655
(226 MiB / 45.7% CPU)
5,655
(226 MiB / 45.7% CPU)
3,237
(119 MiB / 21.8% CPU)
3,237
(119 MiB / 21.8% CPU)
4,311
(82 MiB / 21.4% CPU)
4,311
(82 MiB / 21.4% CPU)
0
(143 MiB / 21.7% CPU)
5,171
(143 MiB / 21.7% CPU)
4,151
(171 MiB / 44.8% CPU)
4,151
(171 MiB / 44.8% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 4,469
(237 MiB / 44.5% CPU)
4,469
(237 MiB / 44.5% CPU)
1,131
(116 MiB / 24.1% CPU)
1,131
(116 MiB / 24.1% CPU)
Not measured Not measured Not measured Not measured 3,737
(247 MiB / 51.0% CPU)
3,737
(247 MiB / 51.0% CPU)

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

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

Windows heavier lossy

Userspace 5 ms one-way delay + 1% TCP connection stall (H1/H2) or UDP datagram drop (H3); 64 KiB GET. Median of 3 repeats on windows-latest @ 803a69bc — 34394872353 (compare-lossy).

Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
HTTP/1 · TLS HTTP/1 · plain 663
(107 MiB / 4.7% CPU)
663
(107 MiB / 4.7% CPU)
635
(141 MiB / 20.3% CPU)
635
(141 MiB / 20.3% CPU)
Not possible Not possible Not possible Not possible 🥇 663
(116 MiB / 4.9% CPU)
663
(116 MiB / 4.9% CPU)
HTTP/2 · TLS HTTP/1 · plain 0
(138 MiB / 1.8% CPU)
87
(138 MiB / 1.8% CPU)
0
(141 MiB / 0.7% CPU)
17
(141 MiB / 0.7% CPU)
Not possible Not possible Not possible Not possible 0
(99 MiB / 1.0% CPU)
16
(99 MiB / 1.0% CPU)
HTTP/3 · QUIC HTTP/1 · plain 0
(67 MiB / 0.0% CPU)
0
(67 MiB / 0.0% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 0
(76 MiB / 0.0% CPU)
0
(76 MiB / 0.0% CPU)

TWP H2 HOL leads (~3.31× YARP). H3 is the protocol-shape win vs H2 HOL on the same lossy session; Win H3 GHA remains 0 (laptop remeasure kept above).

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

Linux heavier lossy

Median of 3 repeats @ 803a69bc. Source: 34394872353 (compare-lossy; lossy H3 uses quic-http3).

Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
HTTP/1 · TLS HTTP/1 · plain 1,208
(144 MiB / 13.2% CPU)
1,208
(144 MiB / 13.2% CPU)
🥇 1,214
(99 MiB / 11.0% CPU)
1,214
(99 MiB / 11.0% CPU)
1,213
(83 MiB / 6.1% CPU)
1,213
(83 MiB / 6.1% CPU)
1,202
(127 MiB / 8.2% CPU)
1,202
(127 MiB / 8.2% CPU)
1,203
(154 MiB / 16.1% CPU)
1,203
(154 MiB / 16.1% CPU)
HTTP/2 · TLS HTTP/1 · plain 🥇 320
(187 MiB / 6.7% CPU)
320
(187 MiB / 6.7% CPU)
40
(99 MiB / 0.3% CPU)
40
(99 MiB / 0.3% CPU)
41
(84 MiB / 0.2% CPU)
41
(84 MiB / 0.2% CPU)
40
(136 MiB / 0.3% CPU)
40
(136 MiB / 0.3% CPU)
40
(127 MiB / 1.4% CPU)
40
(127 MiB / 1.4% CPU)
HTTP/3 · QUIC HTTP/1 · plain 🥇 336
(150 MiB / 12.7% CPU)
336
(150 MiB / 12.7% CPU)
93
(107 MiB / 2.1% CPU)
93
(107 MiB / 2.1% CPU)
Not measured Not measured Not measured Not measured 336
(179 MiB / 20.4% CPU)
336
(179 MiB / 20.4% CPU)

TWP H2 HOL ≫ YARP (~7.7×). H3 TWP÷YARP ≈ 1×.

Architecture-sensitive

Windows heavier architecture-sensitive workloads

Linux heavier architecture-sensitive workloads

compare-arch isolates slow app readers, origin-early response, H2 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 @ 803a69bc (34394884256) (compare-arch). Slow consumer = 256 KiB GET, 16 KiB read + 8 ms sleep. Early response = 64 KiB POST, origin writes after 8 KiB. Duplex H2 = overlapping 64 KiB POST on H2 TLS↔H2 TLS. WebSocket = echo round-trips/sec.

compare-lossy (slow network) is already published above; it is not a slow app reader.

Windows

Windows heavier arch

Scenario Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
Slow consumer (256 KiB GET, throttled client read) HTTP/1 · TLS HTTP/1 · plain 244
(101 MiB / 4.4% CPU)
244
(101 MiB / 4.4% CPU)
203
(143 MiB / 24.7% CPU)
203
(143 MiB / 24.7% CPU)
Not possible Not possible Not possible Not possible 🥇 248
(110 MiB / 4.9% CPU)
248
(110 MiB / 4.9% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 248
(117 MiB / 4.4% CPU)
248
(117 MiB / 4.4% CPU)
161
(141 MiB / 24.7% CPU)
161
(141 MiB / 24.7% CPU)
Not possible Not possible Not possible Not possible 🥇 254
(110 MiB / 6.9% CPU)
254
(110 MiB / 6.9% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 🥇 272
(95 MiB / 18.2% CPU)
272
(95 MiB / 18.2% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 259
(164 MiB / 21.4% CPU)
259
(164 MiB / 21.4% CPU)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 🥇 5,710
(99 MiB / 40.6% CPU)
5,710
(99 MiB / 40.6% CPU)
360
(142 MiB / 24.7% CPU)
360
(142 MiB / 24.7% CPU)
Not possible Not possible Not possible Not possible 3,320
(137 MiB / 47.9% CPU)
3,320
(137 MiB / 47.9% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 🥇 4,085
(187 MiB / 35.4% CPU)
4,085
(187 MiB / 35.4% CPU)
0
(143 MiB / 24.8% CPU)
336
(143 MiB / 24.8% CPU)
Not possible Not possible Not possible Not possible 2,637
(134 MiB / 39.8% CPU)
2,637
(134 MiB / 39.8% CPU)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 🥇 2,235
(147 MiB / 41.9% CPU)
2,235
(147 MiB / 41.9% CPU)
Not possible (no QUIC) Not possible (no QUIC) Not possible Not possible Not possible Not possible 1,915
(201 MiB / 51.8% CPU)
1,915
(201 MiB / 51.8% CPU)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 0
(96 MiB / 0.0% CPU)
0
(96 MiB / 0.0% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 0
(116 MiB / 0.2% CPU)
0
(116 MiB / 0.2% CPU)
Duplex (WebSocket / extended CONNECT) HTTP/1 · TLS HTTP/1 · plain 🥇 30,338
(96 MiB / 41.7% CPU)
30,338
(96 MiB / 41.7% CPU)
17,356
(142 MiB / 24.5% CPU)
17,356
(142 MiB / 24.5% CPU)
Not possible Not possible Not possible Not possible 28,601
(89 MiB / 44.2% CPU)
28,601
(89 MiB / 44.2% CPU)

Linux

Linux heavier arch

Scenario Client Origin TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
Slow consumer (256 KiB GET, throttled client read) HTTP/1 · TLS HTTP/1 · plain 466
(121 MiB / 9.6% CPU)
466
(121 MiB / 9.6% CPU)
415
(100 MiB / 9.9% CPU)
415
(100 MiB / 9.9% CPU)
🥇 472
(81 MiB / 5.6% CPU)
472
(81 MiB / 5.6% CPU)
471
(139 MiB / 6.2% CPU)
471
(139 MiB / 6.2% CPU)
424
(148 MiB / 15.1% CPU)
424
(148 MiB / 15.1% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/2 · TLS HTTP/1 · plain 473
(146 MiB / 19.7% CPU)
473
(146 MiB / 19.7% CPU)
459
(99 MiB / 21.9% CPU)
459
(99 MiB / 21.9% CPU)
🥇 474
(84 MiB / 7.4% CPU)
474
(84 MiB / 7.4% CPU)
469
(156 MiB / 7.4% CPU)
469
(156 MiB / 7.4% CPU)
473
(146 MiB / 24.5% CPU)
473
(146 MiB / 24.5% CPU)
Slow consumer (256 KiB GET, throttled client read) HTTP/3 · QUIC HTTP/1 · plain 🥇 472
(130 MiB / 33.5% CPU)
472
(130 MiB / 33.5% CPU)
0
(119 MiB / 22.6% CPU)
305
(119 MiB / 22.6% CPU)
Not measured Not measured Not measured Not measured 470
(198 MiB / 39.5% CPU)
470
(198 MiB / 39.5% CPU)
Early response (origin writes after first request chunk) HTTP/1 · TLS HTTP/1 · plain 4,694
(139 MiB / 47.6% CPU)
4,694
(139 MiB / 47.6% CPU)
3,048
(100 MiB / 46.8% CPU)
3,048
(100 MiB / 46.8% CPU)
🥇 5,338
(82 MiB / 39.9% CPU)
5,338
(82 MiB / 39.9% CPU)
0
(129 MiB / 8.8% CPU)
650
(129 MiB / 8.8% CPU)
3,170
(178 MiB / 56.2% CPU)
3,170
(178 MiB / 56.2% CPU)
Early response (origin writes after first request chunk) HTTP/2 · TLS HTTP/1 · plain 🥇 3,128
(236 MiB / 46.9% CPU)
3,128
(236 MiB / 46.9% CPU)
0
(111 MiB / 24.1% CPU)
1,332
(111 MiB / 24.1% CPU)
2,125
(83 MiB / 23.7% CPU)
2,125
(83 MiB / 23.7% CPU)
0
(147 MiB / 23.4% CPU)
2,522
(147 MiB / 23.4% CPU)
2,262
(170 MiB / 49.1% CPU)
2,262
(170 MiB / 49.1% CPU)
Early response (origin writes after first request chunk) HTTP/3 · QUIC HTTP/1 · plain 🥇 2,873
(203 MiB / 44.9% CPU)
2,873
(203 MiB / 44.9% CPU)
0
(116 MiB / 24.8% CPU)
442
(116 MiB / 24.8% CPU)
Not measured Not measured Not measured Not measured 2,110
(260 MiB / 48.0% CPU)
2,110
(260 MiB / 48.0% CPU)
Duplex (both directions live) HTTP/2 · TLS HTTP/2 · TLS 0
(100 MiB / 0.1% CPU)
0
(100 MiB / 0.1% CPU)
Not possible Not possible Not possible Not possible Not possible Not possible 0
(149 MiB / 0.3% CPU)
0
(149 MiB / 0.3% CPU)
Duplex (WebSocket / extended CONNECT) HTTP/1 · TLS HTTP/1 · plain 30,840
(126 MiB / 44.0% CPU)
30,840
(126 MiB / 44.0% CPU)
🥇 33,431
(99 MiB / 35.5% CPU)
33,431
(99 MiB / 35.5% CPU)
32,680
(81 MiB / 38.3% CPU)
32,680
(81 MiB / 38.3% CPU)
0
(127 MiB / 40.5% CPU)
0
(127 MiB / 40.5% CPU)
27,895
(125 MiB / 44.3% CPU)
27,895
(125 MiB / 44.3% CPU)

Slow consumer is sleep-bound; H1/H2/H3 sit in the same band. Early-response H1/H2/H3: TWP leads (H1 early ≈ 2.00× / 1.47× YARP Win/Linux). Duplex H2: YARP leads by design — Win ≈ 0.59× (1,270 / 2,135), Linux ≈ 0.15× (282 / 1,882); irreducible concurrent-copier cell (see IO model). WebSocket: TWP÷YARP Windows ≈ 1.06×; Linux nginx leads.

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 TLS termination cost

Linux TLS termination cost

Windows

Median of 3 repeats on windows-latest @ 803a69bc. Source: Actions 34394878841 (compare-tls-cost). Absolute RPS on GHA swings hard; prefer TWP÷YARP.

Windows heavier TLS cost

Workload TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
Keep-alive · tiny GET 🥇 19,208
(88 MiB / 48.1% CPU)
19,208
(88 MiB / 48.1% CPU)
8,700
(139 MiB / 24.9% CPU)
8,700
(139 MiB / 24.9% CPU)
Not possible Not possible Not possible Not possible 17,590
(102 MiB / 51.3% CPU)
17,590
(102 MiB / 51.3% CPU)
New-connection · tiny GET 🥇 702
(89 MiB / 9.1% CPU)
702
(89 MiB / 9.1% CPU)
0
(140 MiB / 24.2% CPU)
239
(140 MiB / 24.2% CPU)
Not possible Not possible Not possible Not possible 683
(115 MiB / 10.5% CPU)
683
(115 MiB / 10.5% CPU)
Keep-alive · 256 KiB GET 🥇 2,710
(124 MiB / 44.6% CPU)
2,710
(124 MiB / 44.6% CPU)
0
(142 MiB / 24.6% CPU)
148
(142 MiB / 24.6% CPU)
Not possible Not possible Not possible Not possible 2,583
(133 MiB / 47.2% CPU)
2,583
(133 MiB / 47.2% CPU)

Linux

Median of 3 repeats @ 803a69bc. Source: Actions 34394878841 (compare-tls-cost).

Linux heavier TLS cost

Workload TWP sustain TWP peak nginx sustain nginx peak HAProxy sustain HAProxy peak Envoy sustain Envoy peak YARP sustain YARP peak
Keep-alive · tiny GET 23,413
(110 MiB / 50.0% CPU)
23,413
(110 MiB / 50.0% CPU)
27,894
(99 MiB / 42.0% CPU)
27,894
(99 MiB / 42.0% CPU)
🥇 29,008
(83 MiB / 42.7% CPU)
29,008
(83 MiB / 42.7% CPU)
16,871
(126 MiB / 59.3% CPU)
16,871
(126 MiB / 59.3% CPU)
20,839
(137 MiB / 51.3% CPU)
20,839
(137 MiB / 51.3% CPU)
New-connection · tiny GET 973
(123 MiB / 47.6% CPU)
973
(123 MiB / 47.6% CPU)
1,017
(100 MiB / 44.6% CPU)
1,017
(100 MiB / 44.6% CPU)
943
(81 MiB / 44.7% CPU)
943
(81 MiB / 44.7% CPU)
🥇 1,070
(129 MiB / 41.5% CPU)
1,070
(129 MiB / 41.5% CPU)
962
(151 MiB / 46.4% CPU)
962
(151 MiB / 46.4% CPU)
Keep-alive · 256 KiB GET 2,681
(127 MiB / 36.9% CPU)
2,681
(127 MiB / 36.9% CPU)
1,784
(98 MiB / 53.8% CPU)
1,784
(98 MiB / 53.8% CPU)
🥇 3,014
(82 MiB / 32.8% CPU)
3,014
(82 MiB / 32.8% CPU)
2,707
(145 MiB / 34.2% CPU)
2,707
(145 MiB / 34.2% CPU)
2,103
(165 MiB / 45.8% CPU)
2,103
(165 MiB / 45.8% CPU)

All three workloads are >1.00× YARP on both OS. nginx leads Linux keep-alive tiny and Linux new-connection; TWP is second, YARP third.

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;
};

Clone this wiki locally