Benchmarks: rustle against 3proxy, Squid, gost and Tinyproxy
Every number published by the project, with the hardware, the versions, the tuning and the limits that go with it. One campaign, 2026-10-28.
How it was measured
Auth on, one proxy core and one worker for every proxy, shuffled test order, proxy restarted before each test, warmup run apart, median of the repeats, concurrency sweep per test (best clean level kept, at_conns).
- Proxy under test
- EM-I320E-NVMe, AMD EPYC 8224P
- Load generator
- EM-I320E-NVMe, AMD EPYC 8224P
- Origin and sink
- EM-I220E-NVMe, AMD EPYC 8124P
- Network
- 25 Gbit/s Private Network (VLAN on the NIC), 25 Gbit/s nominal, 23.5Gbit/s measured
- Runs
- 3 repeats of 15 s, medians, seed 2004113323, noise about ±3 to 5 %
- Proxy cores and authentication
- 1 core, authentication on
Versions and tuning
rustle 1.0.0
cargo build --locked --release (LTO, no target-cpu, Cargo.lock: as in release.yml)
- socket_buffer_bytes = 1048576 (set in the bench config; the shipped default is 0, kernel autotuning)
3proxy 1.0.0
from source, Makefile.Linux
- proxy -a -osTCP_NODELAY -ocTCP_NODELAY (documented for small exchanges; -a = no Forwarded header, like the others)
- maxconn, nscache, timeouts
Squid 7.7
from source, upstream's default flags, OpenSSL
- cache deny all, cache_mem 0
- tcp_recv_bufsize 1 MB, read_ahead_gap 1 MB
- forwarded_for delete, via off
- auth helper results cached 2 h
- workers N for N cores
- http_upgrade_request_protocols WebSocket and websocket allow all (authentication stays enforced by http_access)
gost 3.3.0
official image
- GOGC=400
- logger level error (GOST_LOGGER_LEVEL=error)
- Go sizes GOMAXPROCS from the cpuset
Tinyproxy 1.11.3
from source
- MaxClients 200000
- LogLevel Critical
- no Via, no filters
Results
Best clean concurrency level of each proxy, median of 3 runs. “—” means the proxy does not support that mode. 1 core per proxy · authentication on · Scaleway Elastic Metal, Paris, AMD EPYC 8224P · 25 Gbit/s private network (23.5 measured) · median of 3 runs · seed 2004113323 · campaign of 2026-10-28.
Plain HTTP proxy port
| Test | Unit | rustle | 3proxy | Squid | gost | Tinyproxy |
|---|---|---|---|---|---|---|
| HTTP, 1 KB responses | req/s | 55,258 | 21,032 | 15,140 | 4,755 | 7,580 |
| HTTP, 10 KB responses | req/s | 40,783 | 19,169 | 10,814 | 4,436 | 5,669 |
| HTTP, 1 KB responses, a new connection per request | req/s | 27,827 | 5,132 | 7,804 | 4,780 | 7,584 |
| HTTP, 1 MB responses | req/s | 2,624 | 1,784 | 353 | 1,046 | 199 |
| HTTP, 10 MB responses | req/s | 268 | 191 | 33 | 145 | 21 |
| CONNECT tunnels, bulk transfer | Gbit/s | 22.0 | 15.7 | 13.6 | 13.2 | 1.8 |
| CPU cost per request (1 KB) | µs/req | 18.1 | 47.6 | 66.0 | 210.0 | 129.4 |
| Memory with 1,000 open tunnels | MB | 14 | 104 | 150 | 161 | 18 |
| Memory with 5,000 open tunnels | MB | 40 | 499 | 275 | 653 | 61 |
| Memory with 10,000 open tunnels | MB | 54 | 992 | 432 | 1,243 | 115 |
| Memory with 25,000 open tunnels | MB | 95 | 2,461 | 912 | 3,228 | 274 |
HTTPS proxy (resumed TLS sessions)
| Test | Unit | rustle | 3proxy | Squid | gost | Tinyproxy |
|---|---|---|---|---|---|---|
| HTTP, 1 KB responses | req/s | 49,461 | — | 13,509 | 2,528 | — |
| HTTP, 10 KB responses | req/s | 35,460 | — | 9,341 | 2,433 | — |
| HTTP, 1 KB responses, a new connection per request | req/s | 6,119 | — | 2,047 | 2,516 | — |
| HTTP, 1 MB responses | req/s | 1,297 | — | 277 | 615 | — |
| HTTP, 10 MB responses | req/s | 142 | — | 27 | 83 | — |
| CONNECT tunnels, bulk transfer | Gbit/s | 12.0 | — | 4.9 | 7.1 | — |
| Memory with 1,000 open tunnels | MB | 57 | — | 181 | 240 | — |
| Memory with 5,000 open tunnels | MB | 253 | — | 432 | 903 | — |
| Memory with 10,000 open tunnels | MB | 466 | — | 760 | 2,110 | — |
| Memory with 25,000 open tunnels | MB | 810 | — | 2,584 | 4,094 | — |
HTTPS proxy, full handshake on every connection
| Test | Unit | rustle | 3proxy | Squid | gost | Tinyproxy |
|---|---|---|---|---|---|---|
| HTTP, 1 KB responses, a new connection per request, full handshake | req/s | 5,458 | — | 1,922 | 2,246 | — |
HTTP/2 over TLS
| Test | Unit | rustle | 3proxy | Squid | gost | Tinyproxy |
|---|---|---|---|---|---|---|
| HTTP, 1 KB responses | req/s | 33,451 | — | — | 5,231 | — |
| HTTP, 10 KB responses | req/s | 24,789 | — | — | 4,732 | — |
| HTTP, 1 KB responses, a new connection per request | req/s | 4,974 | — | — | 2,187 | — |
| HTTP, 1 MB responses | req/s | 860 | — | — | 447 | — |
| HTTP, 10 MB responses | req/s | 88 | — | — | 50 | — |
| CONNECT tunnels, bulk transfer | Gbit/s | 8.8 | — | — | 7.8 | — |
| Memory with 1,000 open tunnels | MB | 70 | — | — | 225 | — |
| Memory with 5,000 open tunnels | MB | 330 | — | — | 1,245 | — |
| Memory with 10,000 open tunnels | MB | 705 | — | — | 2,639 | — |
| Memory with 25,000 open tunnels | MB | 1,263 | — | — | 5,884 | — |
WebSocket, plain
| Test | Unit | rustle | 3proxy | Squid | gost | Tinyproxy |
|---|---|---|---|---|---|---|
| WebSocket, 64-byte messages | msg/s | 58,506 | 33,136 | 43,710 | 46,808 | — |
| WebSocket round trip, 64-byte messages (p99) | ms | 1.3 | 3.2 | 1.5 | 2.6 | — |
| CPU cost per WebSocket message | µs/msg | 17.1 | 30.2 | 22.9 | 21.4 | — |
| WebSocket, 64 KB messages | Gbit/s | 11.6 | 7.9 | 6.6 | 6.9 | — |
| WebSocket, one connection per exchange | msg/s | 10,740 | 251 | 4,879 | 3,726 | — |
WebSocket over TLS
| Test | Unit | rustle | 3proxy | Squid | gost | Tinyproxy |
|---|---|---|---|---|---|---|
| WebSocket, 64-byte messages | msg/s | 56,955 | — | 36,786 | 44,426 | — |
| WebSocket round trip, 64-byte messages (p99) | ms | 1.3 | — | 1.8 | 2.7 | — |
| WebSocket, 64 KB messages | Gbit/s | 5.8 | — | 2.4 | 3.8 | — |
| WebSocket, one connection per exchange | msg/s | 4,462 | — | 1,755 | 2,190 | — |
More cores
Plain HTTP, 1 KB responses, 1 to 8 proxy cores
| Proxy cores | rustle | 3proxy | Squid | gost | Tinyproxy |
|---|---|---|---|---|---|
| 1 | 55,275 | 21,106 | 15,094 | 4,785 | — |
| 2 | 111,587 | 34,381 | 29,568 | 9,178 | — |
| 4 | 218,922 | 67,390 | 59,418 | 17,677 | — |
| 8 | 439,741 | 114,736 | 116,834 | 31,011 | — |
Same hosts, seed and protocol as the single-core matrix, one test (http-1kb), connection levels 64, 256, 1,024 and 4,096, best clean level kept. The proxy gets n physical cores and n workers.
Kernel TLS is slower
tls.ktls is opt-in and off by default. Software kernel TLS saves 5 % to 20 % of memory, but is slower than the default rustls path on every throughput test, by 12 % to 43 %. One core, no NIC TLS offload.
| Test | Unit | rustls (default) | Kernel TLS | Change |
|---|---|---|---|---|
| HTTP, 1 KB responses | req/s | 49,461 | 43,422 | -12.2 % |
| HTTP, 10 KB responses | req/s | 35,460 | 31,154 | -12.1 % |
| HTTP, 1 KB responses, a new connection per request | req/s | 6,119 | 5,158 | -15.7 % |
| HTTP, 1 MB responses | req/s | 1,297 | 896 | -30.9 % |
| HTTP, 10 MB responses | req/s | 142 | 89 | -37.1 % |
| CONNECT tunnels, bulk transfer | Gbit/s | 12.0 | 6.9 | -43.0 % |
| Memory with 1,000 open tunnels | MB | 57 | 54 | -5.2 % |
| Memory with 5,000 open tunnels | MB | 253 | 228 | -9.9 % |
| Memory with 10,000 open tunnels | MB | 466 | 372 | -20.3 % |
| Memory with 25,000 open tunnels | MB | 810 | 753 | -7.0 % |
What these numbers do not show
- The origin is static with Content-Length. Chunked bodies, uploads, SOCKS5, IPv6, many origins and large header sets are not part of the matrix; they are covered functionally by the end-to-end suite.
- One campaign, one seed, three repeats, medians, noise about ±3 to 5 %, no confidence intervals. Differences below the noise are not conclusions.
- Latency is closed-loop at each concurrency level (no open-loop rate); CPU per request is measured with the proxy fully loaded, not at the same request rate for every proxy.
- The test network can carry about 23.5 Gbit/s (25 nominal). Where rustle gets close to it (1 MB and 10 MB responses, plain tunnels), the network sets the number, not rustle: rustle's real figure, and its real lead, are higher than the one shown.
- There is no splice on a TLS port (the proxy decrypts in user space), so TLS and HTTP/2 throughput is lower than plain HTTP.
- The HTTP/2 test opens one connection per client connection and sends sequential requests (no multiplexing).
- Multi-core was measured for plain HTTP at 1 KB only; TLS, HTTP/2 and tunnels were not measured on several cores.
- Proxy CPU is the container cgroup's (user + system); softirq time spent on other cores is not attributed.
- The tunnel test counts bytes written by the client and measures the upload direction only.
The raw runs, the harness and the procedure are in the benchmarks directory of the proxy repository.