Description
repost of nodejs/node-v0.x-archive#7902 to ensure it is not lost, as per @jasnell suggestion.
credit goes to @joliss I am merely transplanting the issue.
The fs.realpath function is 70x slower than native C realpath. On my system, fs.realpath takes 32 µs, while C realpath takes 0.45 µs.
This is a real problem in the Broccoli build tool, where we need to resolve symlinks in hot code paths. Resolving 1000 symlinked files - not an unusual case - would take 45 ms, slowing down the build considerably. [1]
As for a solution: I haven't looked at the fs.js source in detail, but it seems we might be able to call the realpath function in the C standard library, where available, instead of using our own implementation.
Benchmark code for Node:
var fs = require('fs')
var start = Date.now()
var n = 10000
for (var i = 0; i < n; i++) {
if (fs.realpathSync('.') === 'dummy') throw new Error('never happens')
}
console.log(((Date.now() - start) * 1000 / n) + ' us') // => 32 us on Node 0.11.13
Benchmark code for C:
#include <limits.h> /* PATH_MAX */
#include <stdio.h>
#include <stdlib.h>
// Adapted from http://stackoverflow.com/a/1563237/525872
int main(void) {
char buf[PATH_MAX + 1]; /* not sure about the "+ 1" */
int i;
for (i = 0; i < 1000000; i++) {
char *res = realpath(".", buf);
if (res) {
// printf("This source is at %s.\n", buf);
} else {
perror("realpath");
exit(EXIT_FAILURE);
}
}
return 0;
}
Run with gcc -std=gnu99 realpath-benchmark.c -o realpath-benchmark && time ./realpath-benchmark
. This yields 0.45 µs per iteration on my Linux system.
[1] We cannot work around this by using naïve string concatenation, because path_resolution(7) requires that we resolve symlinks in all path components. Here is a gist to show why this matters.