Linux::Event::HTTP benchmarking
HTTP/1 throughput was compared with the former implementation before the 0.004 release. The measured gap is documented, not a fixed release target. Never compare results from different machines as a direct A/B result.
Rebuilt end-to-end benchmark
After perl Makefile.PL && make, run:
perl -Mblib bench/run-unblock-end-to-end.pl \
--requests 10000 --warmup 1000 --connections 100 \
--pipeline 1 --response-bytes 32 --repeats 2
The same harness accepts --no-native-input to compare the Perl path.
For historical performance, compare with the original 0.003 source (commit
c0bf714e0f08f9031684e2eb4fd70dea3c68449f) on the same runner,
using its bench/run-http-end-to-end.pl.
A manually dispatched CI run can perform a legacy/rebuild/rebuild/legacy
sequence. Ordinary push CI focuses on correctness and packaging.
The new client benchmark is bench/run-rebuild-client.pl.
bench/run-unblock-engine.pl isolates the protocol engine without
TCP socket costs; bench/run-unblock-retention.pl examines retained
memory during server/client request workloads.
Independent Perl HTTP server comparison
For a peer baseline, the development workflow
.github/workflows/http1-peer-benchmark.yml uses the unchanged shared
client from Benchmark::Web
(revision ceb8b2f9c4cd0764441844dd91dacc1240ebc4c7).
It compares the rebuilt Server's normal public API (a fresh Uniform
response for each request) against the original Linux::Event::HTTP,
Feersum, Hyperman and Mojolicious. No Unblock engines were modified.
Validated 2026-10-25 on one GitHub-hosted Ubuntu runner: https://github.com/haxmeister/perl-Linux-Event-HTTP/actions/runs/40211468264
All servers used one process and one application execution slot, 100 persistent HTTP/1.1 loopback connections, pipeline depth 1, a 32-byte HTTP 200 response body, a shared raw Perl client, and rotated target order. GET: 20,000 measured, 2,000 warmup, 5 repeats; POST: 4,096-byte complete body, 10,000 measured, 1,000 warmup, 3 repeats. The table shows the median requests per second:
| Server | GET /bench | POST /bench, 4 KiB | | --- | ---: | ---: | | Hyperman | 111,289 | 67,543 | | Feersum | 108,622 | 99,643 | | Original Linux::Event::HTTP | 40,413 | 34,617 | | Linux::Event::HTTP (Unblock rebuild) | 9,666 | 7,991 | | Mojolicious | 2,483 | 2,334 |
This is a comparative protocol-stack workload, not identical application-level processing across frameworks. The rebuild passes canonical Uniform messages through a generic transaction lifecycle; other adapters use their normal documented APIs. The result identifies a meaningful throughput gap against the faster Perl servers but does not, on its own, establish a defect in the Unblock::HTTP1 engine or justify rewriting the shared architecture.
These absolute numbers apply to that hosted runner only. Do not
treat them as deployment capacity or cross-machine rankings. The
complete JSON records and latency percentiles are preserved in the
GitHub Actions artifact stage-b-http1-perl-peers. Historical
bench/results/ JSON files are retained in Git but deliberately
excluded from the CPAN source archive. The benchmark-only adapter
also remains in Git, not in the shipped distribution.
Cost-boundary diagnostics
perl -Mblib bench/run-unblock-cost-breakdown.pl \
--iterations 12000 --rounds 3
This measures canonical Uniform::HTTP::Response construction, wire-plan
work, direct Unblock::HTTP1 and the upper Unblock facade under matched
conditions. The temporary
--diagnostic-prebuilt-response option of the end-to-end harness
uses a single frozen canonical Response only to bound constructor cost.
It is not a production caching recommendation; engines must not reuse or
mutate application responses behind callers.
Recorded evidence in Git (not shipped in the source archive):
bench/results/unblock-stage-b/20261025-cost-boundary.json
and bench/results/unblock-stage-b/20261025-bodyless.json,
plus the active status and handoff documents.
In the paired runs so far, the upper Unblock facade added no meaningful
per-request penalty versus direct HTTP1. Native input outperformed the
Perl bridge, but the rebuilt real-socket throughput remained well below
the legacy baseline, which is a measured trade-off rather than an unresolved release blocker.
HTTP/2 h2spec, HTTP/3 QUIC/TLS, WebSocket and generic CONNECT regressions are protocol correctness checks, independent of HTTP/1 throughput.
Published-stack validation and historical reproduction
Current CI installs Unblock::HTTP 0.03, HTTP1 0.13, HTTP2 0.13 and HTTP3 0.14 from CPAN. Historical Linux::Event::HTTP comparisons use the frozen original 0.003 source at c0bf714e0f08f9031684e2eb4fd70dea3c68449f.
The original development-stack workflows and exact dependency pins are preserved at root revision 476f2e84bfcff2d326c46a75a8a9b690c0c3cca4. Use that revision's workflow/dependency set to reproduce the old HTTP1 engine before/after experiment (06a0f5d7c0509ad0deaebe4a8269c03fbe07ea14 versus 4d19f59196481ae4460207c5b22c5125fdbfe186, upper layer e9efa51ee0110227945628f3f85a3feedba90558). Do not prepend old engine Perl sources to the published 0.13 XS installation: their version checks correctly reject that mixed installation. Current correctness CI and current benchmarks use the complete published stack instead.
Server-only and independent native CPU profiles
--nytprof profiles only the server event loop, excluding the load generator
and setup. It needs the optional developer tool Devel::NYTProf (tested with
6.16). Run ordinary benchmarks without profiling for throughput; profiler
instrumentation substantially changes absolute costs and relative weights.
On Linux, clock=2 selects process CPU time rather than time waiting in epoll.
The denominator below includes the 1,000 warmup requests.
NYTPROF=file=/tmp/socket.nytprof:addpid=1:start=no:stmts=0:calls=2:clock=2 \
perl -Mblib -d:NYTProf bench/run-unblock-end-to-end.pl \
--nytprof --requests 10000 --warmup 1000 --repeats 1
perl tools/http1-profile-summary.pl /tmp/socket.nytprof.CHILD_PID 11000
NYTPROF=file=/tmp/engine.nytprof:start=no:stmts=0:calls=2:clock=2 \
perl -d:NYTProf bench/run-unblock-engine.pl \
--case engine_input_and_response --native-input --nytprof --requests 10000
perl tools/http1-profile-summary.pl /tmp/engine.nytprof 11000
The independent engine case uses HTTP1 0.13's own diagnostic BorrowedDriver,
without any Linux::Event module or socket. The helper is private upstream
benchmark machinery, not an application API or a production dependency.
Omit --native-input for ordinary portable input. --case prevents separate
constructor/control benchmarks from contaminating the engine profile.
The summary reports exclusive subroutine time and calls per request;
inclusive times in JSON overlap and must not be added. C work without its
own Perl frame can be charged to its enclosing call, so namespace shares
are attribution evidence, not an exact limit on possible optimization.
Detailed historical profiler data is retained in Git. These diagnostics are not required for application use.