node/benchmark/webstreams/creation.js
Matteo Collina fa2eaa54fb
stream: reduce allocations on WHATWG streams hot paths
Pure-JavaScript optimizations to lib/internal/webstreams/* that reduce
per-chunk and per-construction allocations on hot paths without
observable behavior change.

Per-chunk: reuse promise-reaction closures per controller, add buffered
fast path for async iterator, specialize callback wrappers by arity,
and share immutable nil records for writable stream resets.

Per-construction: use queueMicrotask for non-object start results,
materialize reader/writer .closed and .ready records lazily, and remove
dead allocations.

Assisted-by: Claude Fable 5
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/63876
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Mattias Buelens <mattias@buelens.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
2026-06-20 14:19:08 +00:00

112 lines
2.8 KiB
JavaScript

'use strict';
const common = require('../common.js');
const {
ReadableStream,
ReadableStreamDefaultReader,
ReadableStreamBYOBReader,
TransformStream,
WritableStream,
} = require('node:stream/web');
const assert = require('assert');
const bench = common.createBenchmark(main, {
n: [50e3],
kind: [
'ReadableStream',
'TransformStream',
'WritableStream',
'ReadableStreamDefaultReader',
'ReadableStreamBYOBReader',
'ReadableStream.tee',
],
}, {
// Each case collects garbage right before bench.start() so that the
// timed window measures the work under test rather than leftover
// GC work from the setup phase.
flags: ['--expose-gc'],
});
let readableStream;
let transformStream;
let writableStream;
let readableStreamDefaultReader;
let readableStreamBYOBReader;
let teeResult;
function main({ n, kind }) {
switch (kind) {
case 'ReadableStream':
globalThis.gc();
bench.start();
for (let i = 0; i < n; ++i)
readableStream = new ReadableStream();
bench.end(n);
// Avoid V8 deadcode (elimination)
assert.ok(readableStream);
break;
case 'WritableStream':
globalThis.gc();
bench.start();
for (let i = 0; i < n; ++i)
writableStream = new WritableStream();
bench.end(n);
// Avoid V8 deadcode (elimination)
assert.ok(writableStream);
break;
case 'TransformStream':
globalThis.gc();
bench.start();
for (let i = 0; i < n; ++i)
transformStream = new TransformStream();
bench.end(n);
// Avoid V8 deadcode (elimination)
assert.ok(transformStream);
break;
case 'ReadableStreamDefaultReader': {
const readers = Array.from({ length: n }, () => new ReadableStream());
globalThis.gc();
bench.start();
for (let i = 0; i < n; ++i)
readableStreamDefaultReader = new ReadableStreamDefaultReader(readers[i]);
bench.end(n);
// Avoid V8 deadcode (elimination)
assert.ok(readableStreamDefaultReader);
break;
}
case 'ReadableStreamBYOBReader': {
const readers = Array.from({ length: n }, () => new ReadableStream({ type: 'bytes' }));
globalThis.gc();
bench.start();
for (let i = 0; i < n; ++i)
readableStreamBYOBReader = new ReadableStreamBYOBReader(readers[i]);
bench.end(n);
// Avoid V8 deadcode (elimination)
assert.ok(readableStreamBYOBReader);
break;
}
case 'ReadableStream.tee': {
const streams = Array.from({ length: n }, () => new ReadableStream());
globalThis.gc();
bench.start();
for (let i = 0; i < n; ++i)
teeResult = streams[i].tee();
bench.end(n);
// Avoid V8 deadcode (elimination)
assert.ok(teeResult);
break;
}
default:
throw new Error('Invalid kind');
}
}