fs.promises.readFile = util.promisify(fs.readFile);I think this is where fs.promises.readFile reads the actual file https://github.com/nodejs/node/blob/master/lib/internal/fs/p..., with many layers of indirection before that
fs.readFile is this one function: https://github.com/nodejs/node/blob/master/lib/fs.js#L327-L3...
The 'one function' you linked to just passes a ReadFileContext into binding.open[1]. The ReadFileContext appears to have its own read that uses its own allocated Buffers[2] under some circumstances, but under others they are mysteriously already there[3]. Then binding.open refers to an internalBinding of fs[4], which I'm unsure of, is that js or C++ code? This[5] suggests C++ is involved, but there is also an open in lib/fs.js[6] with that signature, though it also just calls binding.open at the end with the same signature, so I think it's off into C++ by then. At that point, I lose the trail. Maybe C++ is even doing the allocating.
This is not easy code to grok in its full extent.
1 - https://github.com/nodejs/node/blob/master/lib/fs.js#L356
2 - https://github.com/nodejs/node/blob/master/lib/internal/fs/r...
3 - https://github.com/nodejs/node/blob/master/lib/internal/fs/r...
4 - https://github.com/nodejs/node/blob/master/lib/fs.js#L74
5 - https://github.com/nodejs/node/blob/3b2863db12818778671713b5...
6 - https://github.com/nodejs/node/blob/master/lib/fs.js#L472
edit: I was curious so I kept reading, It seems like open is probably C++ here[7], which makes an AsyncCall or SyncCall to "open", which are just wrappers around syscalls[8]. Still not sure about other places the buffer comes from, though.
7 - https://github.com/nodejs/node/blob/master/src/node_file.cc#...
8 - https://github.com/nodejs/node/blob/606df7c4e79324b9725bfcfe...
I’m guessing as disk speeds have increased the relative overhead of dispatching the task to a thread pool has also increased.
At least, that’s how I think it works based on some half-remembered blog post.
Edit: yep - http://docs.libuv.org/en/v1.x/design.html#file-i-o
You’d need to benchmark another non-io bound function to find how much overhead comes from promises themselves.
"My guess is that the promise version is idling when IO capacity is available due to scheduling? Is it implemented in an event driven way internally, and if it is (for example using epoll) is the epoll event able to interrupt and cause the promise to continue to be evaluated or does it have to wait for the vm to get around to scheduling it again?"
It didn't occur to me that the async promise is rescheduled. That right there is a concern.
The OP also points out buffer concatenation, which is an odd choice. Reading the file into memory in one go should start with an stat and then a single buffer alloc. But that is probably naive.
Looking at you, svn team.