Skip to content

Commit b14fd1a

Browse files
committed
lib: speed up require(), phase 1
Replace calls to fs.statSync() with an internal variant that does not create Error or Stat objects that put strain on the garbage collector. A secondary benefit is that it improves start-up times in the debugger because it no longer emits thousands of exception debug events. PR-URL: #1801 Reviewed-By: Trevor Norris <[email protected]>
1 parent 98649fd commit b14fd1a

File tree

2 files changed

+35
-20
lines changed

2 files changed

+35
-20
lines changed

lib/module.js

+14-20
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const runInThisContext = require('vm').runInThisContext;
66
const assert = require('assert').ok;
77
const fs = require('fs');
88
const path = require('path');
9+
const internalModuleStat = process.binding('fs').internalModuleStat;
910

1011

1112
// If obj.hasOwnProperty has been overridden, then calling
@@ -56,13 +57,6 @@ const debug = Module._debug;
5657
// -> a.<ext>
5758
// -> a/index.<ext>
5859

59-
function statPath(path) {
60-
try {
61-
return fs.statSync(path);
62-
} catch (ex) {}
63-
return false;
64-
}
65-
6660
// check if the directory is a package.json dir
6761
const packageMainCache = {};
6862

@@ -94,7 +88,7 @@ function tryPackage(requestPath, exts) {
9488
if (!pkg) return false;
9589

9690
var filename = path.resolve(requestPath, pkg);
97-
return tryFile(filename, null) || tryExtensions(filename, exts) ||
91+
return tryFile(filename) || tryExtensions(filename, exts) ||
9892
tryExtensions(path.resolve(filename, 'index'), exts);
9993
}
10094

@@ -104,18 +98,19 @@ function tryPackage(requestPath, exts) {
10498
Module._realpathCache = {};
10599

106100
// check if the file exists and is not a directory
107-
function tryFile(requestPath, stats) {
108-
stats = stats || statPath(requestPath);
109-
if (stats && !stats.isDirectory()) {
110-
return fs.realpathSync(requestPath, Module._realpathCache);
111-
}
112-
return false;
101+
function tryFile(requestPath) {
102+
const rc = internalModuleStat(requestPath);
103+
return rc === 0 && toRealPath(requestPath);
104+
}
105+
106+
function toRealPath(requestPath) {
107+
return fs.realpathSync(requestPath, Module._realpathCache);
113108
}
114109

115110
// given a path check a the file exists with any of the set extensions
116111
function tryExtensions(p, exts) {
117112
for (var i = 0, EL = exts.length; i < EL; i++) {
118-
var filename = tryFile(p + exts[i], null);
113+
var filename = tryFile(p + exts[i]);
119114

120115
if (filename) {
121116
return filename;
@@ -150,11 +145,10 @@ Module._findPath = function(request, paths) {
150145
var filename;
151146

152147
if (!trailingSlash) {
153-
var stats = statPath(basePath);
154-
// try to join the request to the path
155-
filename = tryFile(basePath, stats);
156-
157-
if (!filename && stats && stats.isDirectory()) {
148+
const rc = internalModuleStat(basePath);
149+
if (rc === 0) { // File.
150+
filename = toRealPath(basePath);
151+
} else if (rc === 1) { // Directory.
158152
filename = tryPackage(basePath, exts);
159153
}
160154

src/node_file.cc

+21
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,26 @@ Local<Value> BuildStatsObject(Environment* env, const uv_stat_t* s) {
433433
return handle_scope.Escape(stats);
434434
}
435435

436+
// Used to speed up module loading. Returns 0 if the path refers to
437+
// a file, 1 when it's a directory or < 0 on error (usually -ENOENT.)
438+
// The speedup comes from not creating thousands of Stat and Error objects.
439+
static void InternalModuleStat(const FunctionCallbackInfo<Value>& args) {
440+
Environment* env = Environment::GetCurrent(args);
441+
442+
CHECK(args[0]->IsString());
443+
node::Utf8Value path(env->isolate(), args[0]);
444+
445+
uv_fs_t req;
446+
int rc = uv_fs_stat(env->event_loop(), &req, *path, nullptr);
447+
if (rc == 0) {
448+
const uv_stat_t* const s = static_cast<const uv_stat_t*>(req.ptr);
449+
rc = !!(s->st_mode & S_IFDIR);
450+
}
451+
uv_fs_req_cleanup(&req);
452+
453+
args.GetReturnValue().Set(rc);
454+
}
455+
436456
static void Stat(const FunctionCallbackInfo<Value>& args) {
437457
Environment* env = Environment::GetCurrent(args);
438458

@@ -1141,6 +1161,7 @@ void InitFs(Handle<Object> target,
11411161
env->SetMethod(target, "rmdir", RMDir);
11421162
env->SetMethod(target, "mkdir", MKDir);
11431163
env->SetMethod(target, "readdir", ReadDir);
1164+
env->SetMethod(target, "internalModuleStat", InternalModuleStat);
11441165
env->SetMethod(target, "stat", Stat);
11451166
env->SetMethod(target, "lstat", LStat);
11461167
env->SetMethod(target, "fstat", FStat);

0 commit comments

Comments
 (0)