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
fix(benchmarks): quote the median run, not the best, and use the CI numbers
The first CI run showed why best-of-N is the wrong statistic here: node-tls-client
ranged from 900 to 3095 req/s across its eleven runs, so its best would have put
it above impit while its median sits a third below. That spread is intrinsic to
the client rather than machine noise, which is exactly the case best-of-N
flatters. The table now quotes the median, the result files keep best and worst,
and a client whose runs swing by more than 1.5x gets a footnote saying so.
The committed numbers are now the ones the workflow measured on a runner instead
of the ones from a loaded laptop.
Copy file name to clipboardExpand all lines: README.md
+17-15Lines changed: 17 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,35 +34,37 @@ async fn main() {
34
34
<!-- comparison:start -->
35
35
### Comparison
36
36
37
-
Sequential requests from a single client against the local HTTP/2 origin in [`benchmarks/`](benchmarks), 1 KiB JSON response, best of 11 runs of 2000 requests. Every client negotiated h2. Each one keeps a single connection warm for the whole run unless a footnote says otherwise. `Profiles` counts the distinct impersonation targets each public API accepts, ignoring aliases that resolve to another target. Python sizes are the platform wheel; Node.js sizes are what `npm install <package>` leaves on disk, transitive dependencies included.
37
+
Sequential requests from a single client against the local HTTP/2 origin in [`benchmarks/`](benchmarks), 1 KiB JSON response, median of 11 runs of 2000 requests. Every client negotiated h2. Each one keeps a single connection warm for the whole run unless a footnote says otherwise. `Profiles` counts the distinct impersonation targets each public API accepts, ignoring aliases that resolve to another target. Python sizes are the platform wheel; Node.js sizes are what `npm install <package>` leaves on disk, transitive dependencies included.
Measured on linux-x64 with CPython 3.12.13 and Node.js v24.16.0 on 2026-08-31. Hardware moves these numbers around, so rerun `benchmarks/` yourself before drawing conclusions.
60
+
Measured on linux-x64 with CPython 3.12.3 and Node.js v24.19.0 on 2026-08-31. Hardware moves these numbers around, so rerun `benchmarks/` yourself before drawing conclusions.
61
61
62
62
[^1]: `primp` does not expose its profile list, and an unknown name silently falls back to a random profile rather than erroring, so the set cannot be counted.
63
63
[^2]: `got-scraping` matches cipher suite and signature algorithm order only; it has no control over extension order, GREASE, or HTTP/2 `SETTINGS`. Its three profiles are not enumerable through the public API, so this count is hard-coded from its bundled cipher table.
64
-
[^3]: `cycletls` opens a new connection for every request, so its figure includes a TLS handshake each time instead of reusing a warm one.
65
-
[^4]: `cycletls` is configured with a raw JA3 string instead of named profiles, so it has no fixed set to count.
64
+
[^3]: `node-tls-client` was erratic across runs — 900 to 3095 req/s — so its median says less than the others'.
65
+
[^4]: `cycletls` was erratic across runs — 409 to 640 req/s — so its median says less than the others'.
66
+
[^5]: `cycletls` opens a new connection for every request, so its figure includes a TLS handshake each time instead of reusing a warm one.
67
+
[^6]: `cycletls` is configured with a raw JA3 string instead of named profiles, so it has no fixed set to count.
0 commit comments