diff --git a/benchmark/fs/bench-read.js b/benchmark/fs/bench-read.js new file mode 100644 index 00000000000000..ba58fee823f718 --- /dev/null +++ b/benchmark/fs/bench-read.js @@ -0,0 +1,59 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const tmpdir = require('../../test/common/tmpdir'); +tmpdir.refresh(); + +const bufferSize = 1024; +const sectorSize = 512; + +const bench = common.createBenchmark(main, { + type: ['existing', 'non-existing'], + n: [1e4], +}); + +function main({ n, type }) { + let fd; + + const tmpfile = { name: tmpdir.resolve(`.existing-file-${process.pid}`), + len: bufferSize * n }; + + + tmpfile.contents = Buffer.allocUnsafe(tmpfile.len); + + for (let offset = 0; offset < tmpfile.len; offset += sectorSize) { + const fillByte = 256 * Math.random(); + const nBytesToFill = Math.min(sectorSize, tmpfile.len - offset); + tmpfile.contents.fill(fillByte, offset, offset + nBytesToFill); + } + + fs.writeFileSync(tmpfile.name, tmpfile.contents); + + switch (type) { + case 'existing': + fd = fs.openSync(tmpfile.name, 'r', 0o666); + break; + case 'non-existing': + fd = 1 << 30; + break; + default: + new Error('Invalid type'); + } + + const buffer = Buffer.alloc(bufferSize); + + + let i = 0; + function readSome() { + if (i++ === n) { + bench.end(n); + if (type === 'existing') fs.closeSync(fd); + } else { + fs.read(fd, buffer, readSome); + } + } + + bench.start(); + readSome(); +} diff --git a/lib/fs.js b/lib/fs.js index 618e48a2b892be..37721c1c93b877 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -595,6 +595,8 @@ function openAsBlob(path, options = kEmptyObject) { * ) => any} callback * @returns {void} */ + +const fsReadCallbackPool = []; function read(fd, buffer, offsetOrOptions, length, position, callback) { fd = getValidatedFd(fd); @@ -660,13 +662,20 @@ function read(fd, buffer, offsetOrOptions, length, position, callback) { validatePosition(position, 'position', length); } - function wrapper(err, bytesRead) { - // Retain a reference to buffer so that it can't be GC'ed too soon. - callback(err, bytesRead || 0, buffer); + let req = fsReadCallbackPool.pop(); + if (req == null) { + req = new FSReqCallback() + req.oncomplete = (err, bytesRead) => { + req.callback(err, bytesRead || 0, req.buffer); + req.callback = null; + req.buffer = null; + // TODO (fix): req.asyncReset something? + fsReadCallbackPool.push(req); + } } - const req = new FSReqCallback(); - req.oncomplete = wrapper; + req.buffer = buffer; + req.callback = callback; binding.read(fd, buffer, offset, length, position, req); }