stream: drop per-chunk Promise alloc in pipeTo#63572
Open
mcollina wants to merge 1 commit into
Open
Conversation
Replace setPromiseHandled with markPromiseAsHandled at the two pipeTo
per-chunk call sites in readablestream.js (the fast-path drain loop and
PipeToReadableStreamReadRequest's chunk handler).
setPromiseHandled chains a `.then(undefined, () => {})` to the writer's
per-chunk write Promise, purely to suppress its unhandled-rejection
event. That chain Promise and the noop closure have no other observer
in this code path: errors already propagate to pipeTo through the
writer.closed promise that watchErrored monitors, and the LAST
state.currentWrite is awaited by waitForCurrentWrite during shutdown
(which still throws because markAsHandled only suppresses the event,
not the rejection state — subsequent .then chains still observe it).
markPromiseAsHandled sets V8's MarkAsHandled + MarkAsSilent flags
directly via the util binding, with no per-chunk JS allocation.
`benchmark/webstreams/pipe-to.js` at HWM=1024 improves by ~5% on my
workstation (728K vs 694K ops/s mean over 6 alternating samples), and
notably exhibits much tighter run-to-run variance because the per-chunk
allocations no longer perturb GC timing.
WPT streams parity preserved: 1403 subtests passing, 0 unexpected
failures.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #63572 +/- ##
==========================================
+ Coverage 90.32% 90.34% +0.01%
==========================================
Files 730 730
Lines 234209 234478 +269
Branches 43934 43911 -23
==========================================
+ Hits 211558 211842 +284
+ Misses 14372 14364 -8
+ Partials 8279 8272 -7
🚀 New features to boost your workflow:
|
aduh95
reviewed
May 26, 2026
Comment on lines
+1574
to
+1582
| // | ||
| // setPromiseHandled's job here is purely to silence the per-chunk | ||
| // unhandled-rejection event — the chain Promise + noop closure it | ||
| // would allocate are unobserved by anything else (errors propagate | ||
| // through writer.closed, which pipeTo monitors via watchErrored). | ||
| // markPromiseAsHandled sets V8's MarkAsHandled + MarkAsSilent flags | ||
| // directly, with no per-chunk allocation. The LAST state.currentWrite | ||
| // is awaited in waitForCurrentWrite during shutdown — markAsHandled | ||
| // does not affect that subsequent .then chain. |
Contributor
There was a problem hiding this comment.
I don't think this comment will make much sense after this PR is merged
Suggested change
| // | |
| // setPromiseHandled's job here is purely to silence the per-chunk | |
| // unhandled-rejection event — the chain Promise + noop closure it | |
| // would allocate are unobserved by anything else (errors propagate | |
| // through writer.closed, which pipeTo monitors via watchErrored). | |
| // markPromiseAsHandled sets V8's MarkAsHandled + MarkAsSilent flags | |
| // directly, with no per-chunk allocation. The LAST state.currentWrite | |
| // is awaited in waitForCurrentWrite during shutdown — markAsHandled | |
| // does not affect that subsequent .then chain. |
aduh95
reviewed
May 26, 2026
| kPending, | ||
| }, | ||
| getPromiseDetails, | ||
| markPromiseAsHandled, |
Contributor
There was a problem hiding this comment.
Does it really make sense to import it just to re-export it? I don't like it, it makes it harder to realize it crosses the C++ boundary
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.
Replace
setPromiseHandledwithmarkPromiseAsHandledat the two pipeTo per-chunk call sites inreadablestream.js. The.then(undefined, () => {})chain thatsetPromiseHandledallocates per chunk has no observer in pipeTo: errors already propagate throughwriter.closed(whichwatchErroredmonitors) and the laststate.currentWriteis awaited inwaitForCurrentWriteduring shutdown (markAsHandled does not affect that subsequent.then). The per-chunk chain Promise + noop closure exist solely to silence the unhandled-rejection event —markPromiseAsHandledsets V8'sMarkAsHandled+MarkAsSilentflags directly with no allocation.benchmark/webstreams/pipe-to.jsat HWM=1024: ~5% throughput improvement (728K vs 694K ops/s mean, 6 alternating samples), with substantially tighter run-to-run variance.WPT streams parity preserved: 1403 subtests passing, 0 unexpected failures.