stream: speed up async iteration over WHATWG byte streams#64291
stream: speed up async iteration over WHATWG byte streams#64291mcollina wants to merge 2 commits into
Conversation
|
Review requested:
|
for await / reader.read() loops over byte streams were ~4x slower than over default streams. Three per-chunk costs, none required by the spec: - ArrayBufferViewGetBuffer/ByteLength/ByteOffset went through ReflectGet(view.constructor.prototype, ...), a reflective get that is ~3.5x slower than the original prototype getters from primordials and spoofable through a user-defined .constructor to boot. - The buffered fast paths in ReadableStreamDefaultReader.read() and the async iterator only covered default controllers, so byte streams with queued data still allocated a read request and PromiseWithResolvers per chunk. Byte-queue dequeue is fully synchronous (it is the queue-filled arm of the byte controller's pull steps), so both fast paths now resolve directly from the byte queue. - readableByteStreamControllerEnqueue re-ran the reader brand check and re-loaded the read request list four times per chunk across HasDefaultReader / ProcessReadRequestsUsingQueue / GetNumReadRequests / FulfillReadRequest; it now does a single pass. The async iterator also reuses its read request object across reads (at most one is ever in flight). benchmark/webstreams interleaved same-day A/B, --runs 10: readable-async-iterator bytes +16.3% (***), readable-read byob +9.1% (***), all other rows neutral. Profiler harness: parked byte iteration +14%, buffered byte iteration +37%, buffered byte read loop +18%, default-stream rows at parity. WPT streams/compression/encoding subtests identical to baseline. Signed-off-by: Matteo Collina <hello@matteocollina.com>
Signed-off-by: Matteo Collina <hello@matteocollina.com>
2782884 to
fa8ad29
Compare
|
CI: https://ci.nodejs.org/job/node-test-pull-request/74561/ Results |
Commit Queue failed- Loading data for nodejs/node/pull/64291 β Done loading data for nodejs/node/pull/64291 ----------------------------------- PR info ------------------------------------ Title stream: speed up async iteration over WHATWG byte streams (#64291) Author Matteo Collina <matteo.collina@gmail.com> (@mcollina) Branch mcollina:webstream-byte-iteration-perf -> nodejs:main Labels author ready, needs-ci, web streams Commits 2 - stream: speed up async iteration over WHATWG byte streams - benchmark: add bytes variant to webstreams async-iterator Committers 1 - Matteo Collina <hello@matteocollina.com> PR-URL: https://github.com/nodejs/node/pull/64291 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> ------------------------------ Generated metadata ------------------------------ PR-URL: https://github.com/nodejs/node/pull/64291 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> -------------------------------------------------------------------------------- βΉ This PR was created on Sat, 04 Jul 2026 20:42:20 GMT β Approvals: 3 β - Stephen Belanger (@Qard): https://github.com/nodejs/node/pull/64291#pullrequestreview-4631159034 β - Antoine du Hamel (@aduh95) (TSC): https://github.com/nodejs/node/pull/64291#pullrequestreview-4631812436 β - Yagiz Nizipli (@anonrig) (TSC): https://github.com/nodejs/node/pull/64291#pullrequestreview-4632180284 β Last GitHub CI successful βΉ Last Full PR CI on 2026-07-06T08:05:34Z: https://ci.nodejs.org/job/node-test-pull-request/74582/ βΉ Last Benchmark CI on 2026-07-05T17:59:44Z: https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1881/ - Querying data for job/node-test-pull-request/74582/ β Build data downloaded β Last Jenkins CI successful -------------------------------------------------------------------------------- β No git cherry-pick in progress β No git am in progress β No git rebase in progress -------------------------------------------------------------------------------- - Bringing origin/main up to date... From https://github.com/nodejs/node * branch main -> FETCH_HEAD β origin/main is now up-to-date - Downloading patch for 64291 From https://github.com/nodejs/node * branch refs/pull/64291/merge -> FETCH_HEAD β Fetched commits as c9f872154a85..fa8ad294ad5c -------------------------------------------------------------------------------- [main d6d4e1e686] stream: speed up async iteration over WHATWG byte streams Author: Matteo Collina <hello@matteocollina.com> Date: Sat Jul 4 22:41:39 2026 +0200 2 files changed, 118 insertions(+), 51 deletions(-) [main ae0779c2ed] benchmark: add bytes variant to webstreams async-iterator Author: Matteo Collina <hello@matteocollina.com> Date: Sat Jul 4 22:41:39 2026 +0200 1 file changed, 27 insertions(+), 10 deletions(-) β Patches applied There are 2 commits in the PR. Attempting autorebase. (node:479) [DEP0190] DeprecationWarning: Passing args to a child process with shell option true can lead to security vulnerabilities, as the arguments are not escaped, only concatenated. (Use `node --trace-deprecation ...` to show where the warning was created) Rebasing (2/4) Executing: git node land --amend --yes --------------------------------- New Message ---------------------------------- stream: speed up async iteration over WHATWG byte streams
benchmark/webstreams interleaved same-day A/B, --runs 10: Signed-off-by: Matteo Collina <hello@matteocollina.com>
|
for await/reader.read()loops over byte streams were ~4x slower than over default streams:ReflectGet(view.constructor.prototype, ...)-based ArrayBufferView getters with primordial getters (~3.5x faster at the call level, and no longer spoofable via a user-defined.constructor)ReadableStreamDefaultReader.read()and the async iterator to byte controllers, skipping the per-chunk read request +PromiseWithResolverswhen data is queuedbenchmark/webstreams(--runs 10): readable-async-iteratortype=bytes+16.3% (***), readable-readbyob+9.1% (***), all other rows neutral. WPT streams/compression/encoding results unchanged.