|
1 | 1 | # Benchmarks |
2 | 2 |
|
3 | | -High-load benchmark suite for HTTP and TCP proxies. |
| 3 | +## Quick Start |
4 | 4 |
|
5 | | -## Validated Performance (8-core, 32GB RAM) |
| 5 | +Start a backend, then run the benchmark against it: |
6 | 6 |
|
7 | | -| Metric | Result | |
8 | | -|--------|--------| |
9 | | -| **Peak concurrent connections** | 672,348 | |
10 | | -| **Memory at peak** | 23 GB | |
11 | | -| **Memory per connection** | ~33 KB | |
12 | | -| **Connection rate (sustained)** | 18,067/sec | |
13 | | -| **CPU at peak** | ~60% | |
14 | | - |
15 | | -## One-Shot Benchmark (Fresh Linux Droplet) |
16 | | - |
17 | | -```bash |
18 | | -curl -sL https://raw.githubusercontent.com/utopia-php/proxy/dev/benchmarks/bootstrap-droplet.sh | sudo bash |
19 | | -``` |
20 | | - |
21 | | -This installs PHP 8.3 + Swoole, tunes the kernel, and runs all benchmarks automatically. |
22 | | - |
23 | | -## Maximum Connection Stress Test |
24 | | - |
25 | | -```bash |
26 | | -./benchmarks/stress-max.sh |
27 | | -``` |
28 | | - |
29 | | -Pushes the system to maximum concurrent connections. Requires root for kernel tuning. |
30 | | - |
31 | | -## Quick start (HTTP) |
32 | | - |
33 | | -Run the PHP benchmark: |
34 | 7 | ```bash |
| 8 | +# HTTP |
| 9 | +php benchmarks/http-backend.php & |
35 | 10 | php benchmarks/http.php |
36 | | -``` |
37 | | - |
38 | | -Run wrk: |
39 | | -```bash |
40 | | -benchmarks/wrk.sh |
41 | | -``` |
42 | | - |
43 | | -Run wrk2 (fixed rate): |
44 | | -```bash |
45 | | -benchmarks/wrk2.sh |
46 | | -``` |
47 | | - |
48 | | -Compare Swoole HTTP servers (evented vs coroutine): |
49 | | -```bash |
50 | | -benchmarks/compare-http-servers.sh |
51 | | -``` |
52 | 11 |
|
53 | | -## Quick start (TCP) |
54 | | - |
55 | | -Run the TCP benchmark: |
56 | | -```bash |
| 12 | +# TCP |
| 13 | +php benchmarks/tcp-backend.php & |
57 | 14 | php benchmarks/tcp.php |
58 | 15 | ``` |
59 | 16 |
|
60 | | -Compare Swoole TCP servers (evented vs coroutine): |
61 | | -```bash |
62 | | -benchmarks/compare-tcp-servers.sh |
63 | | -``` |
64 | | - |
65 | | -## Presets (HTTP) |
66 | | - |
67 | | -Max throughput, burst: |
68 | | -```bash |
69 | | -WRK_THREADS=16 WRK_CONNECTIONS=5000 WRK_DURATION=30s WRK_URL=http://127.0.0.1:8080/ benchmarks/wrk.sh |
70 | | -``` |
71 | | - |
72 | | -Fixed rate (wrk2): |
73 | | -```bash |
74 | | -WRK2_THREADS=16 WRK2_CONNECTIONS=5000 WRK2_DURATION=30s WRK2_RATE=200000 WRK2_URL=http://127.0.0.1:8080/ benchmarks/wrk2.sh |
75 | | -``` |
76 | | - |
77 | | -PHP benchmark, moderate: |
78 | | -```bash |
79 | | -BENCH_CONCURRENCY=500 BENCH_REQUESTS=50000 php benchmarks/http.php |
80 | | -``` |
81 | | - |
82 | | -## Presets (TCP) |
| 17 | +## HTTP Benchmark |
83 | 18 |
|
84 | | -Connection rate only: |
85 | 19 | ```bash |
86 | | -BENCH_PROTOCOL=mysql BENCH_PORT=15433 BENCH_PAYLOAD_BYTES=0 BENCH_CONCURRENCY=500 BENCH_CONNECTIONS=50000 php benchmarks/tcp.php |
| 20 | +BENCH_CONCURRENCY=5000 BENCH_REQUESTS=2000000 php benchmarks/http.php |
87 | 21 | ``` |
88 | 22 |
|
89 | | -Throughput heavy (payload enabled): |
90 | | -```bash |
91 | | -BENCH_PROTOCOL=mysql BENCH_PORT=15433 BENCH_PAYLOAD_BYTES=65536 BENCH_TARGET_BYTES=17179869184 BENCH_CONCURRENCY=2000 php benchmarks/tcp.php |
92 | | -``` |
| 23 | +| Variable | Default | Description | |
| 24 | +|----------|---------|-------------| |
| 25 | +| `BENCH_HOST` | `localhost` | Target host | |
| 26 | +| `BENCH_PORT` | `8080` | Target port | |
| 27 | +| `BENCH_CONCURRENCY` | `cpu*500` | Concurrent workers | |
| 28 | +| `BENCH_REQUESTS` | `concurrency*500` | Total requests | |
| 29 | +| `BENCH_KEEP_ALIVE` | `true` | Reuse connections | |
| 30 | +| `BENCH_TIMEOUT` | `10` | Request timeout (seconds) | |
93 | 31 |
|
94 | | -## Sustained Load Tests |
| 32 | +## TCP Benchmark |
95 | 33 |
|
96 | | -Sustained mode (continuous connection churn): |
97 | 34 | ```bash |
98 | | -BENCH_DURATION=300 BENCH_CONCURRENCY=4000 BENCH_PAYLOAD_BYTES=0 php benchmarks/tcp-sustained.php |
99 | | -``` |
| 35 | +# Connection rate (no payload) |
| 36 | +BENCH_PAYLOAD_BYTES=0 BENCH_CONNECTIONS=400000 php benchmarks/tcp.php |
100 | 37 |
|
101 | | -Max connections mode (hold connections open): |
102 | | -```bash |
103 | | -BENCH_MODE=max_connections BENCH_TARGET_CONNECTIONS=50000 php benchmarks/tcp-sustained.php |
104 | | -``` |
| 38 | +# Throughput (64KB payload) |
| 39 | +BENCH_PAYLOAD_BYTES=65536 BENCH_TARGET_BYTES=17179869184 php benchmarks/tcp.php |
105 | 40 |
|
106 | | -Hold forever mode (Ctrl+C to stop): |
107 | | -```bash |
108 | | -BENCH_MODE=hold_forever BENCH_TARGET_CONNECTIONS=50000 php benchmarks/tcp-sustained.php |
| 41 | +# Sustained streaming |
| 42 | +BENCH_PERSISTENT=true BENCH_STREAM_DURATION=60 php benchmarks/tcp.php |
109 | 43 | ``` |
110 | 44 |
|
111 | | -## Scaling Test (Multiple Backends) |
| 45 | +| Variable | Default | Description | |
| 46 | +|----------|---------|-------------| |
| 47 | +| `BENCH_HOST` | `localhost` | Target host | |
| 48 | +| `BENCH_PORT` | `5432` | Target port | |
| 49 | +| `BENCH_PROTOCOL` | auto | `postgres` or `mysql` (based on port) | |
| 50 | +| `BENCH_CONCURRENCY` | `cpu*500` | Concurrent workers | |
| 51 | +| `BENCH_CONNECTIONS` | derived | Total connections | |
| 52 | +| `BENCH_PAYLOAD_BYTES` | `65536` | Bytes per connection | |
| 53 | +| `BENCH_TARGET_BYTES` | `8GB` | Total bytes target | |
| 54 | +| `BENCH_PERSISTENT` | `false` | Keep connections open | |
| 55 | +| `BENCH_STREAM_DURATION` | `0` | Stream duration in seconds | |
| 56 | +| `BENCH_TIMEOUT` | `10` | Connection timeout (seconds) | |
112 | 57 |
|
113 | | -To test maximum concurrent connections, run multiple backend/client pairs: |
| 58 | +## Kernel Tuning |
114 | 59 |
|
115 | 60 | ```bash |
116 | | -# Start 16 backends on different ports |
117 | | -for p in $(seq 15432 15447); do |
118 | | - BACKEND_PORT=$p php benchmarks/tcp-backend.php & |
119 | | -done |
120 | | - |
121 | | -# Start 16 clients targeting 40k connections each (640k total) |
122 | | -for p in $(seq 15432 15447); do |
123 | | - BENCH_PORT=$p BENCH_MODE=hold_forever BENCH_TARGET_CONNECTIONS=40000 php benchmarks/tcp-sustained.php & |
124 | | -done |
125 | | - |
126 | | -# Monitor connections |
127 | | -watch -n1 'ss -s | grep estab' |
| 61 | +sudo ./benchmarks/setup.sh # Aggressive (benchmarks) |
| 62 | +sudo ./benchmarks/setup.sh --production # Conservative (production) |
| 63 | +sudo ./benchmarks/setup.sh --persist # Survive reboots |
128 | 64 | ``` |
129 | 65 |
|
130 | | -## Environment variables |
131 | | - |
132 | | -HTTP PHP benchmark (`benchmarks/http.php`): |
133 | | -- `BENCH_HOST` (default `localhost`) |
134 | | -- `BENCH_PORT` (default `8080`) |
135 | | -- `BENCH_CONCURRENCY` (default `max(2000, cpu*500)`) |
136 | | -- `BENCH_REQUESTS` (default `max(1000000, concurrency*500)`) |
137 | | -- `BENCH_TIMEOUT` (default `10`) |
138 | | -- `BENCH_KEEP_ALIVE` (default `true`) |
139 | | -- `BENCH_SAMPLE_TARGET` (default `200000`) |
140 | | -- `BENCH_SAMPLE_EVERY` (optional override) |
141 | | - |
142 | | -TCP PHP benchmark (`benchmarks/tcp.php`): |
143 | | -- `BENCH_HOST` (default `localhost`) |
144 | | -- `BENCH_PORT` (default `5432`) |
145 | | -- `BENCH_PROTOCOL` (`postgres` or `mysql`, default based on port) |
146 | | -- `BENCH_CONCURRENCY` (default `max(2000, cpu*500)`) |
147 | | -- `BENCH_CONNECTIONS` (default derived from payload/target) |
148 | | -- `BENCH_PAYLOAD_BYTES` (default `65536`) |
149 | | -- `BENCH_TARGET_BYTES` (default `8GB`) |
150 | | -- `BENCH_TIMEOUT` (default `10`) |
151 | | -- `BENCH_SAMPLE_TARGET` (default `200000`) |
152 | | -- `BENCH_SAMPLE_EVERY` (optional override) |
153 | | -- `BENCH_PERSISTENT` (default `false`) |
154 | | -- `BENCH_STREAM_BYTES` (default `0`, uses `BENCH_TARGET_BYTES` when persistent) |
155 | | -- `BENCH_STREAM_DURATION` (default `0`) |
156 | | -- `BENCH_ECHO_NEWLINE` (default `false`) |
157 | | - |
158 | | -wrk (`benchmarks/wrk.sh`): |
159 | | -- `WRK_THREADS` (default `cpu`) |
160 | | -- `WRK_CONNECTIONS` (default `1000`) |
161 | | -- `WRK_DURATION` (default `30s`) |
162 | | -- `WRK_URL` (default `http://127.0.0.1:8080/`) |
163 | | -- `WRK_EXTRA` (extra flags) |
164 | | - |
165 | | -wrk2 (`benchmarks/wrk2.sh`): |
166 | | -- `WRK2_THREADS` (default `cpu`) |
167 | | -- `WRK2_CONNECTIONS` (default `1000`) |
168 | | -- `WRK2_DURATION` (default `30s`) |
169 | | -- `WRK2_RATE` (default `50000`) |
170 | | -- `WRK2_URL` (default `http://127.0.0.1:8080/`) |
171 | | -- `WRK2_EXTRA` (extra flags) |
172 | | - |
173 | | -Swoole HTTP compare (`benchmarks/compare-http-servers.sh`): |
174 | | -- `COMPARE_HOST` (default `127.0.0.1`) |
175 | | -- `COMPARE_PORT` (default `8080`) |
176 | | -- `COMPARE_CONCURRENCY` (default `1000`) |
177 | | -- `COMPARE_REQUESTS` (default `100000`) |
178 | | -- `COMPARE_SAMPLE_EVERY` (default `5`) |
179 | | -- `COMPARE_RUNS` (default `1`) |
180 | | -- `COMPARE_BENCH_KEEP_ALIVE` (default `true`) |
181 | | -- `COMPARE_BENCH_TIMEOUT` (default `10`) |
182 | | -- `COMPARE_BACKEND_HOST` (default `127.0.0.1`) |
183 | | -- `COMPARE_BACKEND_PORT` (default `5678`) |
184 | | -- `COMPARE_BACKEND_WORKERS` (optional) |
185 | | -- `COMPARE_WORKERS` (default `8`) |
186 | | -- `COMPARE_DISPATCH_MODE` (default `3`) |
187 | | -- `COMPARE_REACTOR_NUM` (default `16`) |
188 | | -- `COMPARE_BACKEND_POOL_SIZE` (default `2048`) |
189 | | -- `COMPARE_KEEPALIVE_TIMEOUT` (default `10`) |
190 | | -- `COMPARE_OPEN_HTTP2` (default `false`) |
191 | | -- `COMPARE_FAST_ASSUME_OK` (default `true`) |
192 | | -- `COMPARE_SERVER_MODE` (default `base`) |
193 | | - |
194 | | -Swoole TCP compare (`benchmarks/compare-tcp-servers.sh`): |
195 | | -- `COMPARE_HOST` (default `127.0.0.1`) |
196 | | -- `COMPARE_PORT` (default `15433`) |
197 | | -- `COMPARE_PROTOCOL` (default `mysql`) |
198 | | -- `COMPARE_CONCURRENCY` (default `2000`) |
199 | | -- `COMPARE_CONNECTIONS` (default `100000`) |
200 | | -- `COMPARE_PAYLOAD_BYTES` (default `0`) |
201 | | -- `COMPARE_TARGET_BYTES` (default `0`) |
202 | | -- `COMPARE_PERSISTENT` (default `false`) |
203 | | -- `COMPARE_STREAM_BYTES` (default `0`) |
204 | | -- `COMPARE_STREAM_DURATION` (default `0`) |
205 | | -- `COMPARE_ECHO_NEWLINE` (default `false`) |
206 | | -- `COMPARE_TIMEOUT` (default `10`) |
207 | | -- `COMPARE_SAMPLE_EVERY` (default `5`) |
208 | | -- `COMPARE_RUNS` (default `1`) |
209 | | -- `COMPARE_MODE` (`single` or `match`, default `single`) |
210 | | -- `COMPARE_CORO_PROCESSES` (optional override) |
211 | | -- `COMPARE_CORO_REACTOR_NUM` (optional override) |
212 | | -- `COMPARE_BACKEND_HOST` (default `127.0.0.1`) |
213 | | -- `COMPARE_BACKEND_PORT` (default `15432`) |
214 | | -- `COMPARE_BACKEND_WORKERS` (optional) |
215 | | -- `COMPARE_BACKEND_START` (default `true`) |
216 | | -- `COMPARE_WORKERS` (default `8`) |
217 | | -- `COMPARE_REACTOR_NUM` (default `16`) |
218 | | -- `COMPARE_DISPATCH_MODE` (default `2`) |
219 | | - |
220 | | -## Notes |
221 | | - |
222 | | -- For realistic max numbers, run on a tuned Linux host (see `PERFORMANCE.md`). |
223 | | -- Running in Docker on macOS will be bottlenecked by the VM and host networking. |
| 66 | +## Reference Numbers (8-core, 32GB RAM) |
| 67 | + |
| 68 | +| Metric | Result | |
| 69 | +|--------|--------| |
| 70 | +| Peak concurrent connections | 672,348 | |
| 71 | +| Memory per connection | ~33 KB | |
| 72 | +| Connection rate (sustained) | 18,067/sec | |
| 73 | +| CPU at peak | ~60% | |
0 commit comments