212 lines
5.2 KiB
JavaScript
212 lines
5.2 KiB
JavaScript
import { Readable, Writable, Duplex } from 'stream';
|
|
import { source, sink, duplex } from '../index.js';
|
|
|
|
const CHUNK_SIZE = 16384;
|
|
const CHUNK_COUNT = 100000;
|
|
|
|
function createBenchmarkReadable() {
|
|
const data = Buffer.alloc(CHUNK_SIZE, 'x');
|
|
let count = 0;
|
|
return new Readable({
|
|
highWaterMark: 1024 * 1024,
|
|
read() {
|
|
if (count < CHUNK_COUNT) {
|
|
count++;
|
|
this.push(data);
|
|
} else {
|
|
this.push(null);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function createBenchmarkWritable() {
|
|
let bytesWritten = 0;
|
|
const w = new Writable({
|
|
highWaterMark: 1024 * 1024,
|
|
write(chunk, encoding, callback) {
|
|
bytesWritten += chunk.length;
|
|
callback();
|
|
}
|
|
});
|
|
return w;
|
|
}
|
|
|
|
async function benchmarkSource() {
|
|
const readable = createBenchmarkReadable();
|
|
const pushSrc = source(readable);
|
|
const received = [];
|
|
let byteCount = 0;
|
|
|
|
const start = process.hrtime.bigint();
|
|
|
|
await new Promise((resolve, reject) => {
|
|
pushSrc.pipe({
|
|
write(data) {
|
|
byteCount += data.length;
|
|
},
|
|
end(err) {
|
|
if (err) return reject(err);
|
|
resolve();
|
|
},
|
|
get paused() { return false; },
|
|
set paused(v) {}
|
|
});
|
|
});
|
|
|
|
const end = process.hrtime.bigint();
|
|
const duration = Number(end - start) / 1e9;
|
|
const throughput = (byteCount / duration / 1024 / 1024).toFixed(2);
|
|
|
|
return { duration, throughput, bytes: byteCount };
|
|
}
|
|
|
|
async function benchmarkSink() {
|
|
const writable = createBenchmarkWritable();
|
|
const pushSnk = sink(writable);
|
|
const data = Buffer.alloc(CHUNK_SIZE, 'x');
|
|
|
|
const start = process.hrtime.bigint();
|
|
|
|
for (let i = 0; i < CHUNK_COUNT; i++) {
|
|
pushSnk.write(data);
|
|
}
|
|
pushSnk.end();
|
|
|
|
await new Promise((resolve) => {
|
|
writable.on('finish', resolve);
|
|
});
|
|
|
|
const end = process.hrtime.bigint();
|
|
const duration = Number(end - start) / 1e9;
|
|
const bytes = CHUNK_COUNT * CHUNK_SIZE;
|
|
const throughput = (bytes / duration / 1024 / 1024).toFixed(2);
|
|
|
|
return { duration, throughput, bytes };
|
|
}
|
|
|
|
async function benchmarkDuplex() {
|
|
const data = Buffer.alloc(CHUNK_SIZE, 'x');
|
|
let count = 0;
|
|
let byteCount = 0;
|
|
|
|
const duplexStream = new Duplex({
|
|
highWaterMark: 1024 * 1024,
|
|
read() {
|
|
if (count < CHUNK_COUNT) {
|
|
count++;
|
|
this.push(data);
|
|
} else {
|
|
this.push(null);
|
|
}
|
|
},
|
|
write(chunk, encoding, callback) {
|
|
callback();
|
|
}
|
|
});
|
|
|
|
const pushDup = duplex(duplexStream);
|
|
|
|
const start = process.hrtime.bigint();
|
|
|
|
await new Promise((resolve, reject) => {
|
|
pushDup.pipe({
|
|
write(data) {
|
|
byteCount += data.length;
|
|
},
|
|
end(err) {
|
|
if (err) return reject(err);
|
|
resolve();
|
|
},
|
|
get paused() { return false; },
|
|
set paused(v) {}
|
|
});
|
|
});
|
|
|
|
const end = process.hrtime.bigint();
|
|
const duration = Number(end - start) / 1e9;
|
|
const throughput = (byteCount / duration / 1024 / 1024).toFixed(2);
|
|
|
|
return { duration, throughput, bytes: byteCount };
|
|
}
|
|
|
|
async function benchmarkObjectMode() {
|
|
const arr = Array.from({ length: CHUNK_COUNT }, (_, i) => ({ id: i, value: Math.random() }));
|
|
let index = 0;
|
|
|
|
const readable = new Readable({
|
|
objectMode: true,
|
|
highWaterMark: 1024,
|
|
read() {
|
|
if (index < arr.length) {
|
|
this.push(arr[index++]);
|
|
} else {
|
|
this.push(null);
|
|
}
|
|
}
|
|
});
|
|
|
|
const pushSrc = source(readable);
|
|
let itemCount = 0;
|
|
|
|
const start = process.hrtime.bigint();
|
|
|
|
await new Promise((resolve, reject) => {
|
|
pushSrc.pipe({
|
|
write(data) {
|
|
itemCount++;
|
|
},
|
|
end(err) {
|
|
if (err) return reject(err);
|
|
resolve();
|
|
},
|
|
get paused() { return false; },
|
|
set paused(v) {}
|
|
});
|
|
});
|
|
|
|
const end = process.hrtime.bigint();
|
|
const duration = Number(end - start) / 1e9;
|
|
const throughput = (itemCount / duration).toFixed(0);
|
|
|
|
return { duration, throughput, items: itemCount };
|
|
}
|
|
|
|
async function runBenchmarks() {
|
|
console.log('stream-to-push-stream benchmarks');
|
|
console.log('================================');
|
|
console.log(`Chunk size: ${CHUNK_SIZE} bytes`);
|
|
console.log(`Chunk count: ${CHUNK_COUNT}`);
|
|
console.log();
|
|
|
|
console.log('Running benchmarks...\n');
|
|
|
|
console.log('1. Source (Readable -> Push-Source):');
|
|
const sourceResult = await benchmarkSource();
|
|
console.log(` Duration: ${sourceResult.duration.toFixed(3)}s`);
|
|
console.log(` Throughput: ${sourceResult.throughput} MB/s`);
|
|
console.log();
|
|
|
|
console.log('2. Sink (Writable -> Push-Sink):');
|
|
const sinkResult = await benchmarkSink();
|
|
console.log(` Duration: ${sinkResult.duration.toFixed(3)}s`);
|
|
console.log(` Throughput: ${sinkResult.throughput} MB/s`);
|
|
console.log();
|
|
|
|
console.log('3. Duplex (Duplex -> Push-Duplex):');
|
|
const duplexResult = await benchmarkDuplex();
|
|
console.log(` Duration: ${duplexResult.duration.toFixed(3)}s`);
|
|
console.log(` Throughput: ${duplexResult.throughput} MB/s`);
|
|
console.log();
|
|
|
|
console.log('4. Object Mode (Readable Object -> Push-Source):');
|
|
const objectResult = await benchmarkObjectMode();
|
|
console.log(` Duration: ${objectResult.duration.toFixed(3)}s`);
|
|
console.log(` Throughput: ${objectResult.throughput} items/s`);
|
|
console.log();
|
|
|
|
console.log('Benchmark complete.');
|
|
}
|
|
|
|
runBenchmarks().catch(console.error);
|