Skip to content

Commit 5095bc9

Browse files
committed
stream: avoid per-chunk promises in webstream adapters
Readable.fromWeb() and the read side of Duplex.fromWeb() allocated a promise, a read-result object, and two reaction closures for every chunk through reader.read(). Only one read is ever in flight, so a single reused read request delivers chunks through readableStreamDefaultReaderRead() instead, forwarding each chunk in a microtask to keep the previous delivery order relative to errors and destroy. Writable.fromWeb() and the write side of Duplex.fromWeb() paid two derived promises off writer.ready plus the writer.write() promise and a fresh closure pair per chunk. A single shared write request (the same contract pipeTo uses) now dispatches chunks directly and settles the node callback, with failures delivered in a microtask because the callback can destroy the stream while the writable machinery is mid-transition. Also add benchmark/webstreams/adapters.js; the suite had no rows for the adapter paths. confidence improvement accuracy (*) (**) (***) webstreams/adapters.js kind='readable-from-web' n=100000 * 5.07 % ±4.84% ±6.44% ±8.39% webstreams/adapters.js kind='readable-to-web' n=100000 0.92 % ±6.25% ±8.32% ±10.83% webstreams/adapters.js kind='writable-from-web' n=100000 *** 39.27 % ±7.19% ±9.58% ±12.48% webstreams/adapters.js kind='writable-to-web' n=100000 -1.74 % ±6.71% ±8.94% ±11.63% Signed-off-by: Matteo Collina <hello@matteocollina.com>
1 parent 7aaf9b4 commit 5095bc9

3 files changed

Lines changed: 266 additions & 85 deletions

File tree

‎benchmark/webstreams/adapters.js‎

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
'use strict';
2+
const common = require('../common.js');
3+
const {
4+
Readable,
5+
Writable,
6+
} = require('node:stream');
7+
const {
8+
ReadableStream,
9+
WritableStream,
10+
} = require('node:stream/web');
11+
12+
const bench = common.createBenchmark(main, {
13+
n: [1e5],
14+
kind: [
15+
'readable-to-web',
16+
'readable-from-web',
17+
'writable-to-web',
18+
'writable-from-web',
19+
],
20+
});
21+
22+
async function readableToWeb(n) {
23+
const chunk = Buffer.alloc(1024);
24+
let i = 0;
25+
const streamReadable = new Readable({
26+
read() {
27+
if (i++ < n)
28+
this.push(chunk);
29+
else
30+
this.push(null);
31+
},
32+
});
33+
const reader = Readable.toWeb(streamReadable).getReader();
34+
bench.start();
35+
while (!(await reader.read()).done);
36+
bench.end(n);
37+
}
38+
39+
function readableFromWeb(n) {
40+
const chunk = Buffer.alloc(1024);
41+
let i = 0;
42+
const readableStream = new ReadableStream({
43+
pull(controller) {
44+
if (i++ < n)
45+
controller.enqueue(chunk);
46+
else
47+
controller.close();
48+
},
49+
});
50+
const streamReadable = Readable.fromWeb(readableStream);
51+
bench.start();
52+
streamReadable.on('data', () => {});
53+
streamReadable.on('end', () => bench.end(n));
54+
}
55+
56+
async function writableToWeb(n) {
57+
const chunk = Buffer.alloc(1024);
58+
const streamWritable = new Writable({
59+
write(chunk, encoding, callback) {
60+
callback();
61+
},
62+
});
63+
const writer = Writable.toWeb(streamWritable).getWriter();
64+
bench.start();
65+
for (let i = 0; i < n; i++)
66+
await writer.write(chunk);
67+
await writer.close();
68+
bench.end(n);
69+
}
70+
71+
function writableFromWeb(n) {
72+
const chunk = Buffer.alloc(1024);
73+
const writableStream = new WritableStream({
74+
write() {},
75+
});
76+
const streamWritable = Writable.fromWeb(writableStream);
77+
bench.start();
78+
let i = 0;
79+
function writeLoop() {
80+
while (i++ < n) {
81+
if (!streamWritable.write(chunk)) {
82+
streamWritable.once('drain', writeLoop);
83+
return;
84+
}
85+
}
86+
streamWritable.end(() => bench.end(n));
87+
}
88+
writeLoop();
89+
}
90+
91+
function main({ n, kind }) {
92+
switch (kind) {
93+
case 'readable-to-web':
94+
readableToWeb(n);
95+
break;
96+
case 'readable-from-web':
97+
readableFromWeb(n);
98+
break;
99+
case 'writable-to-web':
100+
writableToWeb(n);
101+
break;
102+
case 'writable-from-web':
103+
writableFromWeb(n);
104+
break;
105+
}
106+
}

0 commit comments

Comments
 (0)