Skip to content

Commit

Permalink
lib: speed up require(), phase 1
Browse files Browse the repository at this point in the history
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 <trev.norris@gmail.com>
  • Loading branch information
bnoordhuis committed May 27, 2015
1 parent 98649fd commit b14fd1a
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 20 deletions.
34 changes: 14 additions & 20 deletions lib/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const runInThisContext = require('vm').runInThisContext;
const assert = require('assert').ok;
const fs = require('fs');
const path = require('path');
const internalModuleStat = process.binding('fs').internalModuleStat;


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

function statPath(path) {
try {
return fs.statSync(path);
} catch (ex) {}
return false;
}

// check if the directory is a package.json dir
const packageMainCache = {};

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

var filename = path.resolve(requestPath, pkg);
return tryFile(filename, null) || tryExtensions(filename, exts) ||
return tryFile(filename) || tryExtensions(filename, exts) ||
tryExtensions(path.resolve(filename, 'index'), exts);
}

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

// check if the file exists and is not a directory
function tryFile(requestPath, stats) {
stats = stats || statPath(requestPath);
if (stats && !stats.isDirectory()) {
return fs.realpathSync(requestPath, Module._realpathCache);
}
return false;
function tryFile(requestPath) {
const rc = internalModuleStat(requestPath);
return rc === 0 && toRealPath(requestPath);
}

function toRealPath(requestPath) {
return fs.realpathSync(requestPath, Module._realpathCache);
}

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

if (filename) {
return filename;
Expand Down Expand Up @@ -150,11 +145,10 @@ Module._findPath = function(request, paths) {
var filename;

if (!trailingSlash) {
var stats = statPath(basePath);
// try to join the request to the path
filename = tryFile(basePath, stats);

if (!filename && stats && stats.isDirectory()) {
const rc = internalModuleStat(basePath);
if (rc === 0) { // File.
filename = toRealPath(basePath);
} else if (rc === 1) { // Directory.
filename = tryPackage(basePath, exts);
}

Expand Down
21 changes: 21 additions & 0 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,26 @@ Local<Value> BuildStatsObject(Environment* env, const uv_stat_t* s) {
return handle_scope.Escape(stats);
}

// Used to speed up module loading. Returns 0 if the path refers to
// a file, 1 when it's a directory or < 0 on error (usually -ENOENT.)
// The speedup comes from not creating thousands of Stat and Error objects.
static void InternalModuleStat(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

CHECK(args[0]->IsString());
node::Utf8Value path(env->isolate(), args[0]);

uv_fs_t req;
int rc = uv_fs_stat(env->event_loop(), &req, *path, nullptr);
if (rc == 0) {
const uv_stat_t* const s = static_cast<const uv_stat_t*>(req.ptr);
rc = !!(s->st_mode & S_IFDIR);
}
uv_fs_req_cleanup(&req);

args.GetReturnValue().Set(rc);
}

static void Stat(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Expand Down Expand Up @@ -1141,6 +1161,7 @@ void InitFs(Handle<Object> target,
env->SetMethod(target, "rmdir", RMDir);
env->SetMethod(target, "mkdir", MKDir);
env->SetMethod(target, "readdir", ReadDir);
env->SetMethod(target, "internalModuleStat", InternalModuleStat);
env->SetMethod(target, "stat", Stat);
env->SetMethod(target, "lstat", LStat);
env->SetMethod(target, "fstat", FStat);
Expand Down

0 comments on commit b14fd1a

Please sign in to comment.