node/benchmark/fs/bench-filehandle-pipetosync.js
James M Snell e78ccd8017 benchmark: add benchmarks for experimental stream/iter
Signed-off-by: James M Snell <jasnell@gmail.com>
Assisted-By: Claude/Opus 4.6
PR-URL: https://github.com/nodejs/node/pull/62066
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
2026-03-27 19:55:35 -07:00

103 lines
3 KiB
JavaScript

// Benchmark: pipeToSync with sync compression transforms.
// Measures fully synchronous file-to-file pipeline (no threadpool, no promises).
'use strict';
const common = require('../common.js');
const fs = require('fs');
const { openSync, closeSync, writeSync, unlinkSync } = fs;
const tmpdir = require('../../test/common/tmpdir');
tmpdir.refresh();
const srcFile = tmpdir.resolve(`.removeme-sync-bench-src-${process.pid}`);
const dstFile = tmpdir.resolve(`.removeme-sync-bench-dst-${process.pid}`);
const bench = common.createBenchmark(main, {
compression: ['gzip', 'deflate', 'brotli', 'zstd'],
filesize: [1024 * 1024, 16 * 1024 * 1024, 64 * 1024 * 1024],
n: [5],
}, {
flags: ['--experimental-stream-iter'],
});
function main({ compression, filesize, n }) {
// Create the fixture file with repeating lowercase ASCII
const chunk = Buffer.alloc(Math.min(filesize, 64 * 1024), 'abcdefghij');
const fd = openSync(srcFile, 'w');
let remaining = filesize;
while (remaining > 0) {
const toWrite = Math.min(remaining, chunk.length);
writeSync(fd, chunk, 0, toWrite);
remaining -= toWrite;
}
closeSync(fd);
const { pipeToSync } = require('stream/iter');
const {
compressGzipSync,
compressDeflateSync,
compressBrotliSync,
compressZstdSync,
} = require('zlib/iter');
const { open } = fs.promises;
const compressFactory = {
gzip: compressGzipSync,
deflate: compressDeflateSync,
brotli: compressBrotliSync,
zstd: compressZstdSync,
}[compression];
// Stateless uppercase transform (sync)
const upper = (chunks) => {
if (chunks === null) return null;
const out = new Array(chunks.length);
for (let j = 0; j < chunks.length; j++) {
const src = chunks[j];
const buf = Buffer.allocUnsafe(src.length);
for (let i = 0; i < src.length; i++) {
const b = src[i];
buf[i] = (b >= 0x61 && b <= 0x7a) ? b - 0x20 : b;
}
out[j] = buf;
}
return out;
};
// Use a synchronous wrapper since pipeToSync is fully sync.
// We need FileHandle for pullSync/writer, so open async then run sync.
(async () => {
const srcFh = await open(srcFile, 'r');
const dstFh = await open(dstFile, 'w');
// Warm up
runSync(srcFh, dstFh, upper, compressFactory, pipeToSync);
// Reset file positions for the benchmark
await srcFh.close();
await dstFh.close();
bench.start();
let totalBytes = 0;
for (let i = 0; i < n; i++) {
const src = await open(srcFile, 'r');
const dst = await open(dstFile, 'w');
totalBytes += runSync(src, dst, upper, compressFactory, pipeToSync);
await src.close();
await dst.close();
}
bench.end(totalBytes / (1024 * 1024));
cleanup();
})();
}
function runSync(srcFh, dstFh, upper, compressFactory, pipeToSync) {
const w = dstFh.writer();
pipeToSync(srcFh.pullSync(upper, compressFactory()), w);
return w.endSync();
}
function cleanup() {
try { unlinkSync(srcFile); } catch { /* Ignore */ }
try { unlinkSync(dstFile); } catch { /* Ignore */ }
}