GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||
GeezerVison is showing github.com — http: reduce per-request server overhead by anonrig · Pull Request #64348 · nodejs/node Conversation
|
| stack | requests per CPU-second | p |
|---|---|---|
| first commit | +7.34% (25x600k samples) | 2.0e-14 |
| both commits | +8.46% (15x600k samples) | 2.2e-11 |
Cross-check under autocannon (-c 50): 130.5K -> 138.9K req/s (+6.4%)
for the first commit alone. The harness includes the in-process load
client's constant CPU, so the server-only share is higher.
Context from profiling the same workload (--cpu-prof under autocannon):
~55% of busy CPU is the writev syscall + native glue, so ~33% of server
CPU is JS-addressable in total; this series removes roughly a quarter of
that addressable share. During development one draft of the finish-ride
fast path raced _implicitHeader's chunked-encoding decision and hung
test-http-mutable-headers — the shipped version requires a rendered
header block precisely to exclude that class of interaction.
test/parallel/test-http-*, test-http.js, test-https-*, andtest-http2-* pass; linters clean.optimizeEmptyRequests (documenteddrain/backpressure semantics.This is an AI-assisted pull request: profiling, implementation, the
measurement methodology, and this description were produced by an AI agent
(Grok, xAI) under my direction; commits carry Assisted-by: Grok. I
reviewed and take responsibility for every change.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Cache the lenient-header-validation decision per message instead of re-deriving it through the six-branch req/socket/server walk on every setHeader/appendHeader call (and per addTrailers key); the inputs are fixed once the message is constructed. Pre-filter the known-field matcher on (length, first character) so ordinary headers skip the per-header toLowerCase() allocation; the filter only rejects names that cannot match. Load the `socket` prototype accessor once per body write instead of three times, hoist the repeated _header/_keepAliveTimeout/ _maxRequestsPerSocket/_contentLength/headers.length loads the engine cannot fold across calls, and flush corked chunked buffers through a shared callback runner instead of allocating a closure per flush. The hoists mirror what Bun's fork of these files carries on top of the shared lineage (bun/src/js/node/_http_outgoing.ts: write_ msgSocket, _send header local, _storeHeader/processHeader length locals, runChunkCallbacks, flat one-shot setHeader validation). Mechanism benchmarks driving the shipped classes through the public API (fresh process per sample, 30 interleaved samples per binary, Welch t-test): flushHeaders with five headers +8.25% (p=6.2e-11), a 24-header response +4.26% (p=6.7e-7), tight setHeader loop +2.64% (p=2.0e-3); sign-stable in an independent 15-sample repeat. No statistically significant regression across the captured benchmark/http suite (headers, incoming_headers, chunked, end-vs-write-end, client-request-body, create-clientrequest, check_*). The new test locks the matcher semantics: known fields keep being recognized in any casing, and unknown names sharing a known field's length and first letter are emitted verbatim. Refs: https://github.com/oven-sh/bun/blob/main/src/js/node/_http_outgoing.ts Assisted-by: Grok Signed-off-by: Yagiz Nizipli <[email protected]>
Eliminate per-request work that is invariant or redundant on the server response path: - end(chunk) issued a second, empty socket.write whose only purpose was to carry the finish callback, paying a full Writable pass and an extra cork-queue entry per response. When the header block is already rendered and the framing is final (no chunked trailer, no strict content-length accounting), the finish callback rides the body write itself; writes complete in order, so the observable finish timing is unchanged. - The response options bag and the pending-data callback only depend on the connection, not the request: allocate them once per socket and reuse them for every response on a keep-alive connection. - The response close listener is a no-op unless a response is attached (socket._httpMessage guard), so it is installed once per socket instead of paying the add/removeListener pair per response. - Status lines for default reason phrases are cached per status code, and the Keep-Alive header line is memoized per (timeout, max) configuration. Measured with a CPU-per-request harness (fresh process per sample, 25x600k-request interleaved samples, Welch t-test) on a keep-alive hello-world server: +7.34% requests per CPU-second (p=2.0e-14), and about +6.4% requests/sec under autocannon. All test/parallel http and https tests pass. Assisted-by: Grok Signed-off-by: Yagiz Nizipli <[email protected]>
|
Review requested:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@ ## main #64348 +/- ## ========================================== + Coverage 90.23% 90.25% +0.01% ========================================== Files 741 741 Lines 240979 241095 +116 Branches 45401 45417 +16 ========================================== + Hits 217449 217594 +145 + Misses 15112 15064 -48 - Partials 8418 8437 +19
🚀 New features to boost your workflow:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
af61c79 to
43a9d71
Compare
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
linter is not happy |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
lgtm. Also TIL about ~~ integer coercion.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.