A corrected HTTP harness changes the comparison.

A linked-runtime probe found that two earlier campaigns ran one Flyology event loop while reporting 16. With 16 loops observed and every server thread pinned, lightweight and native converge under saturation on local ARM64 and both native Linux architectures; pinned Rust fixtures lead the tier comparisons.

Correction

This entry supersedes the preliminary snapshot and its longer Ada-only follow-up. Their metadata claimed 16 loops, but an Alire-generated build setting overrode the prepared runtime and linked one. They also pinned only the process leader, allowing worker threads to escape the intended server CPU set.

The corrected build removes the overriding setting, links and runs a probe that must report the requested loop count, records requested and observed values separately, and fails if any live server thread has the wrong CPU mask after startup or warmup. Every run here reports 16 requested and 16 observed loops.

The old downloads remain available to preserve the record. Their throughput, lane ordering, CPU, and thread conclusions are not current evidence.

METHOD

Plain engines and routed applications remain separate.

The plain tier compares Flyology's raw handler, hyper's HTTP/1 connection service, and the AWS and EWS callback APIs. The application tier compares Flyology routing, axum, Actix Web, and one identical ServletAda servlet over AWS or EWS. A framework result never enters the plain-engine ranking.

Transport
HTTP/1.1 cleartext loopback with persistent connections.
Responses
Exact 13-byte and 1 KiB plain bodies; exact 13-byte body after one routed GET.
Placement
Server CPUs 0–7, client CPUs 8–15, 16 verified Flyology loops, and eight configured Rust workers.
Local common run
Seven 10-second trials, two-second warmups and cooldowns, concurrency one.
Shared-host common run
Seven 30-second trials, five-second warmups, 20-second cooldowns, concurrency one, on native ARM64 and AMD64.
Validity
Only exact responses with 100% HTTP 200 success and zero timeouts enter common tables.

The Rust side is pinned to Rust 1.97.1, hyper 1.11.0, axum 0.8.9, Actix Web 4.14.0, and Tokio 1.53.1. Direct dependencies use exact versions and Cargo.lock fixes the transitive graph.

The shared hosts used 16-CPU pod quotas, not exclusive CPU sets. The ARM64 host used Neoverse-N1 with 80 physical cores and 256 GB; the AMD64 host used Intel Xeon Gold 5412U with 24 physical cores, 48 logical CPUs, and 256 GB.

The measurement record retains its original hashes. The local run recorded unreachable d0abaa29…. Reachable 6110161d… has the same showcases/http-comparison tree and commit delta, but it is only a showcase-subtree analogue: its Flyology src tree differs by 1,410 additions and 94 deletions across 12 files, including the HTTP server built by the local Docker campaign. It cannot reproduce the measured binary. The report source and Kubernetes overlay recorded unreachable 46a718a4…; reachable 49ef1ca5… is likewise proven identical only for the showcases/http-comparison subtree and the commit delta, not for every packaged overlay or build input. Neither reachable analogue is a historical reproduction checkout. b0cfa28… and the Kubernetes base 7880b4e… were already reachable and remain unchanged in the method record.

The historical raw oha JSON, logs, and complete metadata bundles remained in ignored build/ output and are no longer available. The downloads preserve the published aggregates and provenance record, but they are not sufficient to independently regenerate those aggregates. No measurement was rerun for this provenance correction.

LOCAL GATE

At c128, lightweight was on par with or ahead of native.

The user-set gate for proceeding was more than 100k requests per second from both Flyology lanes at concurrency 128. All three corrected local rows cleared it with 100% success and no timeouts. These are one-second probes, not publishable rankings.

Local ARM64 c128 saturation probe: one trial
WorkloadLightweight req/sNative req/sLW vs nativeLW p99Native p99
Plain, 13 B530.2k505.4k+4.9%3.442 ms0.609 ms
Plain, 1 KiB491.8k490.4k+0.3%3.888 ms0.605 ms
Routed GET445.4k429.7k+3.7%3.737 ms0.673 ms

The throughput result supports lightweight parity at this loop count. The latency result adds an important boundary: lightweight's c128 p99 was 5.6–6.4 times native's. Throughput leadership is not tail-latency leadership.

COMMON PROFILE

Concurrency one favors different implementations than saturation.

These medians compare the same c1 contract in each environment. Cross-machine values are not processor rankings; each row is a separate loopback system.

Plain HTTP, 13-byte body: median requests per second
EnvironmentFlyology LWFlyology nativehyperAWSEWS
Local ARM6421.7k22.9k32.6k16.5k16.7k
Shared ARM6413.7k14.6k22.6k8.1k7.1k
Shared AMD6423.8k27.5k38.1k18.0k10.8k
Routed application GET: median requests per second
EnvironmentFlyology LWFlyology nativeaxumActix WebServletAda/AWSServletAda/EWS
Local ARM6420.6k21.6k32.2k31.8k10.4k15.5k
Shared ARM6412.0k13.2k21.2k21.8k3.6k6.2k
Shared AMD6420.9k24.0k37.2k37.5k9.9k10.2k

hyper led both plain workloads in all three environments. axum led the local routed row; Actix Web narrowly led the two shared-host routed rows. Flyology native was the fastest Ada fixture everywhere, but the corrected result does not support overall throughput leadership. The downloadable CSV gives all ranges and shows that every included row retained 100% success with zero timeouts.

EFFICIENCY

Thread economy remains lightweight's clearest advantage.

With 16 loops, every lightweight server reached 17 threads: the main thread plus the loop pool. Native remained at 257 because the fixture capacity is 256. CPU seconds cover one server lifecycle, so compare lanes within an environment and tier; the local and shared-host lifecycles have different durations.

Flyology process resources: median CPU/RSS and maximum threads
EnvironmentTierLaneCPU secondsRSS MiBThreads
Local ARM64PlainLightweight36.3812.717
PlainNative36.7612.8257
ApplicationLightweight19.5012.817
ApplicationNative19.0313.6257
Shared ARM64PlainLightweight120.488.117
PlainNative114.469.4257
ApplicationLightweight61.238.117
ApplicationNative58.019.4257
Shared AMD64PlainLightweight122.5312.317
PlainNative120.4313.4257
ApplicationLightweight62.4611.617
ApplicationNative61.4413.2257

Lightweight's thread reduction did not become a CPU win here: CPU time was nearly equal locally and native used 2–5% less on the shared hosts. Lightweight did keep a modest RSS advantage. The Rust throughput leaders also used fewer CPU seconds, less RSS, and nine or ten threads in these c1 fixtures. Complete CPU, RSS, thread, throughput-range, p50, p99, p99.9, success, and timeout values for every fixture are in the CSV downloads.

SATURATION PROFILE

More concurrency closes the lane gap, not the tier gap.

The c1 profile above asks about per-request overhead. This separate profile asks what happens as persistent connections rise to c8, c32, and c128. Local ARM64 is a one-second gate; the two native Linux views are medians from three 10-second trials. Every shared-host result plotted below passed all three trials with 100% HTTP 200 responses and zero timeouts.

The short answer

At c128, lightweight was ahead of native on shared ARM64 and effectively tied on shared AMD64. That convergence did not become overall leadership: hyper led both plain workloads and Actix Web led routed dispatch on both shared architectures.

c128 plain HTTP, 13-byte body

Thousands of requests per second; local is one observation, shared results are three-trial medians.

Y: throughput (k req/s) · X: entrant

c128 plain HTTP, 1 KiB body

Thousands of requests per second; the y-axis matches the 13-byte chart.

Y: throughput (k req/s) · X: entrant

c128 routed application GET

Thousands of requests per second; frameworks remain separate from the plain-engine tier.

Y: throughput (k req/s) · X: entrant

c128 plain HTTP, 13-byte body: thousands of requests per second
EnvironmentFlyology LWFlyology nativehyperAWSEWS
Local ARM64, one trial530.2505.4541.131.524.7
Shared ARM64, median245.2223.3582.136.9excluded
Shared AMD64, median355.7356.2785.474.9excluded
c128 plain HTTP, 1 KiB body: thousands of requests per second
EnvironmentFlyology LWFlyology nativehyperAWSEWS
Local ARM64, one trial491.8490.4472.329.813.7
Shared ARM64, median231.6212.3563.935.8excluded
Shared AMD64, median339.2336.3775.273.3excluded
c128 routed GET: thousands of requests per second
EnvironmentFlyology LWFlyology nativeaxumActix WebServletAda/AWSServletAda/EWS
Local ARM64, one trial445.4429.7507.41,689.318.613.8
Shared ARM64, median203.4193.3517.5715.518.2excluded
Shared AMD64, median282.5286.5757.6877.137.3excluded

A tentative interpretation

One plausible reading is that, with 16 verified loops and enough persistent connections in flight, cooperative scheduling no longer keeps lightweight throughput below native: lightweight leads native by 5–10% on shared ARM64 and stays within 1.5% on shared AMD64. That interpretation is incomplete on its own. Lightweight still uses 17 threads instead of 257, but native's c128 p99 is 3.6–6.7 times lower, while hyper and Actix Web retain the highest valid tier throughput. These CPU-small loopback endpoints do not establish the same ordering for database or external-I/O services.

EWS and ServletAda/EWS timed out at c8, c32, and c128 in all three shared-host trials, so they are marked excluded rather than ranked. The aggregate CSV publishes every valid c8/c32/c128 median, range, and latency percentile; the excluded-observation CSV records each timeout-bearing trial; and the resource CSV records CPU, RSS, threads, and context switches.

LIMITS

The result stops at HTTP dispatch.

These endpoints do almost no application work. They do not use TLS, rotate connections, query a database, call a remote service, serialize a meaningful object, or exercise backpressure under external I/O. Do not extrapolate the ordering to those systems.

The shared hosts were lightly loaded when selected, but they were not dedicated. Kubernetes applied a CPU quota rather than an exclusive static CPU set. The process masks separated server and client CPUs, but unrelated host work could still run there. The local ARM64 run also crosses a macOS virtualization boundary and heterogeneous host cores.

Architecture and concurrency change the lane gap: native led lightweight by 5–6% locally at c1, 6–9% on shared ARM64, and 15–18% on shared AMD64. At c128, lightweight led by 0.3–4.9% locally and 5–10% on shared ARM64; the shared AMD64 lanes stayed within 1.5%. At c1 native generally had the lower p99, while lightweight generally had the lower p99.9; no single lane owns every latency tail.

The corrected evidence is narrower than the superseded claim. With 16 verified loops, lightweight offers a 17-thread server and reaches native-like saturation throughput, but native is faster at c1 and has a much lower c128 p99. hyper, axum, and Actix Web establish higher throughput and efficiency reference points for these exact fixtures. None of this predicts a database-backed or external-I/O service.

NEW CAMPAIGN

Pin one complete source tree and retain its evidence.

There is no reachable checkout that is proven to reproduce the dated local binary or complete Kubernetes overlay. The following templates are for a new campaign: replace the placeholder with one reachable commit containing every input to be measured, use that same revision throughout, and retain the sanitized raw output outside ignored build/ paths.

Local ARM64 saturation command
One-second c8, c32, and c128 probe
git switch --detach <reachable-commit-to-measure>
test -z "$(git status --porcelain)"
HTTP_BENCH_TRIALS=1 \
HTTP_BENCH_DURATION=1s \
HTTP_BENCH_WARMUP=1s \
HTTP_BENCH_CONCURRENCIES="8 32 128" \
HTTP_BENCH_COOLDOWN=0 \
HTTP_BENCH_LOOPS=16 \
HTTP_BENCH_SERVER_CPUSET=0-7 \
HTTP_BENCH_CLIENT_CPUSET=8-15 \
HTTP_BENCH_RUST_WORKERS=8 \
HTTP_BENCH_SATURATION_PROBE=1 \
  ./showcases/http-comparison/scripts/run-linux-docker.sh
Seven-trial native Linux command
ARM64 and AMD64 campaign
git switch --detach <reachable-commit-to-measure>
test -z "$(git status --porcelain)"
HTTP_BENCH_GIT_REVISION=<reachable-commit-to-measure> \
HTTP_BENCH_LOCAL_OVERLAY=0 \
HTTP_BENCH_ARM64_NODE=<selected-arm64-node> \
HTTP_BENCH_AMD64_NODE=<selected-amd64-node> \
HTTP_BENCH_TRIALS=7 \
HTTP_BENCH_DURATION=30s \
HTTP_BENCH_WARMUP=5s \
HTTP_BENCH_COOLDOWN=20 \
HTTP_BENCH_CONCURRENCIES=1 \
HTTP_BENCH_LOOPS=16 \
HTTP_BENCH_SERVER_CPUSET=0-7 \
HTTP_BENCH_CLIENT_CPUSET=8-15 \
HTTP_BENCH_RUST_WORKERS=8 \
  ./showcases/http-comparison/scripts/run-kubernetes.sh
Repeated high-concurrency Kubernetes command
Three 10-second trials at c8, c32, and c128
git switch --detach <reachable-commit-to-measure>
test -z "$(git status --porcelain)"
HTTP_BENCH_GIT_REVISION=<reachable-commit-to-measure> \
HTTP_BENCH_LOCAL_OVERLAY=0 \
HTTP_BENCH_ARM64_NODE=<selected-arm64-node> \
HTTP_BENCH_AMD64_NODE=<selected-amd64-node> \
HTTP_BENCH_TRIALS=3 \
HTTP_BENCH_DURATION=10s \
HTTP_BENCH_WARMUP=5s \
HTTP_BENCH_COOLDOWN=5 \
HTTP_BENCH_CONCURRENCIES="8 32 128" \
HTTP_BENCH_LOOPS=16 \
HTTP_BENCH_SERVER_CPUSET=0-7 \
HTTP_BENCH_CLIENT_CPUSET=8-15 \
HTTP_BENCH_RUST_WORKERS=8 \
HTTP_BENCH_SATURATION_PROBE=1 \
  ./showcases/http-comparison/scripts/run-kubernetes.sh

The runner validates response bytes before measurement, rejects any common row with a timeout or non-200 response, preserves raw observations below ignored build/, and verifies deletion of its temporary namespace. Retain and sanitize those raw outputs outside the ignored working directory before publishing a new table. A new final-branch benchmark claim requires a complete local and Kubernetes rerun with that retained bundle. These templates describe a future campaign; they neither reproduce the dated binary nor indicate that a rerun occurred here.