Skip to content

Commit 8c46ff0

Browse files
committed
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.
1 parent 76489f9 commit 8c46ff0

File tree

1 file changed

+23
-31
lines changed

1 file changed

+23
-31
lines changed

src/util/fs.js

+23-31
Original file line numberDiff line numberDiff line change
@@ -520,39 +520,31 @@ 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] = readFile(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+
},
547+
));
556548
},
557549
CONCURRENT_QUEUE_ITEMS,
558550
);

0 commit comments

Comments
 (0)