Skip to content

Commit 7a63e0d

Browse files
zkatarcanis
authored andcommitted
fs: do bulk file reads to optimize cache extraction (#3539)
* fs: do bulk file reads to optimize cache extraction This patch boosts cache extraction by ~2x+ by letting node do more parallelization work. This makes nearly all of the file copy stuff be done by the C++ code with minimal boundary-crossing (at least compared to node streams). Streams in node.js are ~3x slower, specially for small files, than just doing fs.writeFile/readFile, because of this boundary. This is something Yarn might want to take into account in other places. The reason this is OK is because pretty much any files this would handle would fit neatly into memory (any npm packages MUST fit into memory by definition, because of the way npm@<5 does extracts). If you really want to make doubleplus sure to minimize memory usage, you could do an fs.stat to find the file size and then do heuristics to only use streams for files bigger than <X>MB. * Uses readFileBuffer instead of readFile
1 parent 76489f9 commit 7a63e0d

File tree

1 file changed

+24
-31
lines changed

1 file changed

+24
-31
lines changed

src/util/fs.js

+24-31
Original file line numberDiff line numberDiff line change
@@ -520,39 +520,32 @@ export async function copyBulk(
520520
}
521521

522522
const cleanup = () => delete currentlyWriting[data.dest];
523-
return (currentlyWriting[data.dest] = new Promise((resolve, reject) => {
524-
const readStream = fs.createReadStream(data.src);
525-
const writeStream = fs.createWriteStream(data.dest, {mode: data.mode});
526-
527-
reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest));
528-
529-
readStream.on('error', reject);
530-
writeStream.on('error', reject);
531-
532-
writeStream.on('open', function() {
533-
readStream.pipe(writeStream);
534-
});
535-
536-
writeStream.once('close', function() {
537-
fs.utimes(data.dest, data.atime, data.mtime, function(err) {
538-
if (err) {
539-
reject(err);
540-
} else {
541-
events.onProgress(data.dest);
542-
cleanup();
543-
resolve();
544-
}
523+
reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest));
524+
return (currentlyWriting[data.dest] = readFileBuffer(data.src)
525+
.then(d => {
526+
return writeFile(data.dest, d, {mode: data.mode});
527+
})
528+
.then(() => {
529+
return new Promise((resolve, reject) => {
530+
fs.utimes(data.dest, data.atime, data.mtime, err => {
531+
if (err) {
532+
reject(err);
533+
} else {
534+
resolve();
535+
}
536+
});
545537
});
546-
});
547-
})
548-
.then(arg => {
549-
cleanup();
550-
return arg;
551538
})
552-
.catch(arg => {
553-
cleanup();
554-
throw arg;
555-
}));
539+
.then(
540+
() => {
541+
events.onProgress(data.dest);
542+
cleanup();
543+
},
544+
err => {
545+
cleanup();
546+
throw err;
547+
},
548+
));
556549
},
557550
CONCURRENT_QUEUE_ITEMS,
558551
);

0 commit comments

Comments
 (0)