Description
Environment:
- Dell Latitude-5420
- Ubuntu 25.04
- kernel: 6.14.0-15-generic
- PHP 8.4.5 (cli) (built: Mar 17 2025 20:35:32) (NTS)
- amphp/http-server: 3.4.3
Benchmark tools:
- wrk debian/4.1.0-4build2 [epoll] Copyright (C) 2012 Will Glozer
- hey
Description:
When running load tests against a minimal “hello world” PHP script served by amphp/http-server, enabling HTTP keep-alive causes per-request latency to jump to ~40 ms, whereas disabling keep-alive (i.e. forcing Connection: close) yields ~150–200 µs response times. Under keep-alive the PHP worker’s CPU usage is effectively zero.
Context:
I’m currently developing a Symfony Runtime integration for amphp, and during performance testing I observed these elevated response times. At first I assumed the issue was in my own implementation, but switching to the official hello-world.php example reproduced the same behavior, indicating the problem lies within amphp/http-server itself.
Steps to reproduce:
- run
php examples/hello-world.php
- Run wrk with keep-alive (default):
wrk --latency -t 1 -c 1 http://localhost:1337/
Running 10s test @ http://localhost:1337/
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 41.12ms 1.78ms 43.20ms 98.35%
Req/Sec 24.30 4.98 30.00 57.00%
Latency Distribution
50% 41.03ms
75% 41.17ms
90% 42.00ms
99% 42.98ms
243 requests in 10.01s, 42.40KB read
Requests/sec: 24.27
Transfer/sec: 4.24KB
- Run wrk disabling keep-alive:
wrk --header "Connection: Close" --latency -t 1 -c 1 http://localhost:1337/
Running 10s test @ http://localhost:1337/
1 threads and 1 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 150.09us 28.03us 0.87ms 92.33%
Req/Sec 5.52k 434.76 5.90k 90.10%
Latency Distribution
50% 143.00us
75% 151.00us
90% 169.00us
99% 277.00us
55473 requests in 10.10s, 7.88MB read
Requests/sec: 5492.54
Transfer/sec: 799.22KB
- Run wrk with keep-alive (default):
hey -c 1 -z 10s http://127.0.0.1:1337/
Summary:
Total: 10.0128 secs
Slowest: 0.0430 secs
Fastest: 0.0013 secs
Average: 0.0412 secs
Requests/sec: 24.2690
Total data: 3159 bytes
Size/request: 13 bytes
Response time histogram:
0.001 [1] |
0.005 [0] |
0.010 [0] |
0.014 [0] |
0.018 [0] |
0.022 [0] |
0.026 [0] |
0.030 [0] |
0.035 [0] |
0.039 [0] |
0.043 [242] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
Latency distribution:
10% in 0.0409 secs
25% in 0.0410 secs
50% in 0.0411 secs
75% in 0.0419 secs
90% in 0.0420 secs
95% in 0.0421 secs
99% in 0.0429 secs
Details (average, fastest, slowest):
DNS+dialup: 0.0000 secs, 0.0013 secs, 0.0430 secs
DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs
req write: 0.0000 secs, 0.0000 secs, 0.0001 secs
resp wait: 0.0006 secs, 0.0002 secs, 0.0014 secs
resp read: 0.0405 secs, 0.0002 secs, 0.0421 secs
Status code distribution:
[200] 243 responses
- Run hey disabling keep-alive:
hey -c 1 -z 10s -disable-keepalive http://127.0.0.1:1337/
Summary:
Total: 10.0008 secs
Slowest: 0.0025 secs
Fastest: 0.0002 secs
Average: 0.0003 secs
Requests/sec: 3447.0364
Total data: 448149 bytes
Size/request: 13 bytes
Response time histogram:
0.000 [1] |
0.000 [32562] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
0.001 [841] |■
0.001 [272] |
0.001 [696] |■
0.001 [88] |
0.002 [9] |
0.002 [2] |
0.002 [1] |
0.002 [0] |
0.003 [1] |
Latency distribution:
10% in 0.0002 secs
25% in 0.0002 secs
50% in 0.0003 secs
75% in 0.0003 secs
90% in 0.0003 secs
95% in 0.0004 secs
99% in 0.0010 secs
Details (average, fastest, slowest):
DNS+dialup: 0.0001 secs, 0.0002 secs, 0.0025 secs
DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs
req write: 0.0000 secs, 0.0000 secs, 0.0009 secs
resp wait: 0.0001 secs, 0.0001 secs, 0.0012 secs
resp read: 0.0000 secs, 0.0000 secs, 0.0010 secs
Status code distribution:
[200] 34473 responses
Actual behavior:
With HTTP keep-alive:
- Latency per request ~40 ms
- PHP worker idle (≈0% CPU)
Without HTTP keep-alive:
- Latency per request ~150–200 µs
- PHP worker fully utilized
Expected behavior:
Enabling HTTP keep-alive should either reduce or have minimal impact on per-request latency and maintain high throughput; overall request handling performance should be similar whether the connection is reused or not.