[v26.x backport] http2: reduce per-request allocations#64663
Open
mcollina wants to merge 3 commits into
Open
Conversation
Cut several sources of per-stream/per-request overhead on the hot path: - Track 'priority'/'frameError' stream listeners by overriding the EventEmitter methods on Http2Stream instead of subscribing to 'newListener'/'removeListener', which made every listener add and remove on every stream emit an extra tracking event. - Replace the per-call SafeSet and sensitive-header mapping in buildNgHeaderString with a lazily allocated array and an empty-array fast path, and skip the HTTP token regex and connection-specific header checks for well-known single-value header names. - Replace per-call closures with shared named handlers in onStreamClose, afterShutdown and Http2Stream._destroy. - Skip the pendingStreams Set add/delete for streams that are created with their native handle already available (all server streams). - Hoist the per-request onStreamTimeout closure factories in the compat layer to module-level handlers, and avoid a once() wrapper allocation per server stream. h2load, 1 KiB response payload, -c 4 -m 100, mean of 6 alternating runs: core API 60.2k -> 69.3k req/s (+15%), compat API 43.6k -> 46.2k req/s (+5.9%). Signed-off-by: Matteo Collina <hello@matteocollina.com> PR-URL: nodejs#64265 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> Reviewed-By: Tim Perry <pimterry@gmail.com>
Every _write()/_writev() on an Http2Stream allocated four closures and an anonymous nextTick callback to coordinate the write callback with the end-of-stream check. Since the stream machinery dispatches at most one write at a time, that coordination state can live on the stream's kState object instead, with shared named functions for the end check and completion logic. When trailers are pending the writable side cannot be shut down early anyway, so the end-of-stream check tick is now skipped entirely for those writes. Also pre-initialize the kState fields that used to be added dynamically (shutdownWritableCalled, fd) so hot-path stores no longer transition the object shape. h2load, 1 KiB response payload, -c 4 -m 100, mean of 6 alternating runs vs main: core API 61.0k -> 70.7k req/s (+15.9% cumulative), compat API 43.7k -> 50.4k req/s (+15.3% cumulative). Signed-off-by: Matteo Collina <hello@matteocollina.com> PR-URL: nodejs#64265 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> Reviewed-By: Tim Perry <pimterry@gmail.com>
respond() copied the user-provided options object on every call just so it could normalize and locally flip options.endStream, and prepareResponseHeadersObject() then looked the :status and date fields up again on the dictionary-mode null-prototype headers copy it had just built. Use a local variable for endStream and pick up :status/date while copying the headers instead. No measurable throughput change on its own; this removes an object clone and several dictionary-mode property lookups per response. Signed-off-by: Matteo Collina <hello@matteocollina.com> PR-URL: nodejs#64265 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> Reviewed-By: Tim Perry <pimterry@gmail.com>
Collaborator
|
Review requested:
|
metcoder95
approved these changes
Jul 21, 2026
Contributor
|
The commit message does not match the ones that landed on |
mcollina
force-pushed
the
backport-64265-v26.x-staging
branch
from
July 21, 2026 22:16
38d5541 to
6bf6d34
Compare
Member
Author
|
Updated the branch: the retained commits now preserve the messages and timestamps from |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backport the v26-compatible parts of #64265.
Included:
Excluded changes require the HTTP/2 stream-lifecycle work in #63249 or fail v26 regression tests: stream lifecycle/pending-stream changes, trailer-round-trip changes, native auto-empty trailers, and scheduled-callback reductions.
Tests:
make -j4tools/test.py http2test-h2-large-header-cause-client-to-hanguptest-h2leak-destroy-session-on-socket-endedtest-http2-many-writes-and-destroy