You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[perf] TLS handshake rate plateaus at ~640/s and HTTPS costs ~35% throughput vs HTTP #306
Measured CWIST HTTPS performance on a 12-core Linux loopback box. HTTPS costs ~35% of throughput vs plaintext on the same handler, and full TLS 1.3 handshakes plateau at ~640 handshakes/sec regardless of client concurrency (no scaling past ~P32). That is low for a 12-core machine — nginx/OpenSSL-class servers do several thousand full handshakes/sec.
Measured numbers
Benchmark server: cwist_app_use_https() with example/othello-web/server.{crt,key}, one route returning a 20-byte JSON. Tooling: wrk -t4 -c100 -d10s, curl --no-keepalive for handshake rate.
Throughput (keep-alive):
Server
req/sec
p50
p99
CWIST HTTP :8080
290,755
139 µs
4.01 ms
CWIST HTTPS :8443
190,067
250 µs
4.28 ms
Full TLS 1.3 handshakes/sec (curl, loopback):
Concurrency
CWIST HTTPS
openssl s_server (same cert)
P8
~515
—
P32
~643
~212
P64
~635 (plateau)
—
CWIST beats the sequential openssl s_server baseline but stops scaling at ~640/s, suggesting the handshake pipeline (not crypto) is the bottleneck — consistent with open issue #294.
Code findings (src/net/http/https.c)
Good: cert/key loaded once into SSL_CTX (no per-request reload); session resumption + shared pre-fork ticket keys enabled; non-blocking handshakes on sharded epoll shepherd threads.
16 KiB read buffer (include/cwist/net/http/http.h:138) — every SSL_read caps at 16 KiB (https.c:1477-1478); no SSL_set_buffer_size/BIO enlargement anywhere, so large request bodies need many small encrypted reads.
Update: root cause identified — the ~640 handshakes/s plateau was NOT shard-count or crypto limited. It was a Nagle/delayed-ACK stall: clients without TCP_NODELAY hold the HTTP request behind Nagle, and the kernel re-enters pingpong mode during the TLS handshake so the one-shot TCP_QUICKACK set at accept time no longer applies, delaying the client's request ~40ms. Fix (re-arm TCP_QUICKACK around handshake) in PR #307: 705 → 7,199 conn/s at 32 clients, plaintext control unchanged.
Fixed by #307 (TCP_QUICKACK re-arm) and guarded by the HTTPS perf gates (#309, calibrated and green). Churn went from ~640/s plateau to 5-7k conn/s; RTT stall 43ms -> 0.2ms.
Activity
gg582 commentedon Oct 4, 2026
Update: root cause identified — the ~640 handshakes/s plateau was NOT shard-count or crypto limited. It was a Nagle/delayed-ACK stall: clients without TCP_NODELAY hold the HTTP request behind Nagle, and the kernel re-enters pingpong mode during the TLS handshake so the one-shot TCP_QUICKACK set at accept time no longer applies, delaying the client's request ~40ms. Fix (re-arm TCP_QUICKACK around handshake) in PR #307: 705 → 7,199 conn/s at 32 clients, plaintext control unchanged.
feat(https,metrics): TLS observability counters in Prometheus /metric…
perf(https): re-arm TCP_QUICKACK around the TLS handshake to avoid th…
ci(perf): add HTTPS/TLS performance gates for 3.9 (issues #306, #307)
feat(https,metrics): TLS observability counters in Prometheus /metric…
docs(roadmap): add v4.0 readiness section
gg582 commentedon Oct 10, 2026
Fixed by #307 (TCP_QUICKACK re-arm) and guarded by the HTTPS perf gates (#309, calibrated and green). Churn went from ~640/s plateau to 5-7k conn/s; RTT stall 43ms -> 0.2ms.
gg582 commentedon Oct 10, 2026
Nagle again. ew.
docs(roadmap): refresh the v4.0 readiness section to the current state
docs(roadmap): refresh the v4.0 readiness section to the current state