stream: cut promise churn in webstreams hot paths - #65138
Open
mcollina wants to merge 3 commits into
Open
Conversation
The highWaterMark values were passed as properties of the underlying source and sink dictionaries, where they are ignored: a queuing strategy's highWaterMark is read from the constructors' second argument. Every configuration therefore measured the identical workload at the default highWaterMark of 1, which also explains the historically high run-to-run variance of this benchmark family. Pass the strategies as the constructors' second argument and cover the default (1) alongside buffered (1024, 4096) configurations. Signed-off-by: Matteo Collina <hello@matteocollina.com>
Three related reductions on the per-chunk paths: Wrap user sink.write and source.pull callbacks without coercing their result into a promise. When the callback returns a non-thenable (the common synchronous case), fulfillment is guaranteed and no then() lookup is observable, so the fulfilled reaction is enqueued through a single shared resolved promise at the exact microtask position the coerced promise's reaction would have had, skipping the implicit async-wrapper promise per chunk. Thenable results go through PromiseResolve(), which matches the spec's "a promise resolved with" conversion (identity for native promises). Park pipeTo's pump on backpressure by installing a record that duck-types the writer's lazily-materialized [[readyPromise]] record and whose resolve function is the pump continuation itself. Backpressure clearing then resumes the pump directly instead of materializing a fresh promise record plus reaction per flip, and the pump no longer schedules a microtask per batch. writableStreamUpdateBackpressure publishes the new backpressure state before resolving the ready record so the pump observes the updated value. Replace queueMicrotask() on the pipeTo and tee chunk-forwarding paths with a reaction on the shared resolved promise, which enqueues the continuation at the same position without the per-call scheduling overhead. pipe-to improves by 8-14% across all benchmark configurations, with readable-read and tee also improving in spot runs. Signed-off-by: Matteo Collina <hello@matteocollina.com>
Collaborator
|
Review requested:
|
jasnell
reviewed
Aug 8, 2026
jasnell
reviewed
Aug 8, 2026
jasnell
approved these changes
Aug 8, 2026
jasnell
left a comment
Member
There was a problem hiding this comment.
Couple of nits, otherwise LGTM
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #65138 +/- ##
========================================
Coverage 90.30% 90.30%
========================================
Files 759 759
Lines 248294 248415 +121
Branches 46860 46871 +11
========================================
+ Hits 224220 224334 +114
+ Misses 15516 15503 -13
- Partials 8558 8578 +20
🚀 New features to boost your workflow:
|
The start, pull, and write non-op algorithms are all raw callbacks with an identical empty body now, so a single shared nonOpCallback replaces nonOpStart, nonOpPull, and nonOpWrite. Signed-off-by: Matteo Collina <hello@matteocollina.com>
bjohansebas
approved these changes
Aug 9, 2026
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.
Twelfth round of pure-JS webstreams optimizations, following #64890.
Profiling
pipeToat the defaulthighWaterMarkshowed roughly 10% of the profile inqueueMicrotaskplus its native binding (one call per pump batch), a freshwriter.readypromise record plus reaction per backpressure flip on thepipeThroughshape, and an implicit async-wrapper promise persink.write/source.pullinvocation. Three changes, one commit:sink.writeandsource.pullcallbacks are wrapped without coercing their result into a promise. A non-thenable result (the common synchronous case) means fulfillment is guaranteed and nothen()lookup is observable, so the fulfilled reaction is enqueued through a single shared resolved promise at the exact microtask position the coerced promise's reaction would have had. Thenable results go throughPromiseResolve(), matching the reference implementation'spromiseCall(identity for native promises).[[readyPromise]]record;writableStreamUpdateBackpressureresolving it re-enters the pump directly. This removes the per-flip promise record + reaction and the per-batchqueueMicrotask. The backpressure state field is now published before the ready record is resolved so the hook observes the new value (the resolve of a real ready record only settles a promise, so the reorder is unobservable otherwise).forwardChunkhops and the pump's between-batch yield use a reaction on the shared resolved promise instead ofqueueMicrotask, which enqueues at the same position with less overhead.A separate first commit fixes the
pipe-to.jsbenchmark: thehighWaterMarkvalues were passed inside the underlying source/sink dictionaries where they are ignored, so all 16 configurations measured the identical workload at the defaulthighWaterMarkof 1. The strategies are now passed as the constructors' second argument, with the matrix covering the default (1) and buffered (1024, 4096) configurations.Benchmark results with the fixed benchmark (30 runs):
The full-suite run showed no regressions in any other family;
readable-read normal,tee normal, andpipeThroughpassthrough spot runs also improve (~+6-18%). Verified with the WPT streams/compression/encoding suites, the full parallel webstream/whatwg test set, and a shutdown-ordering stress (abort mid-write, close with pending writes, sync-throwing and rejecting sinks, error propagation) whose event log is byte-identical tomain.