lib: speed up require(), phase 2
authorBen Noordhuis <info@bnoordhuis.nl>
Tue, 26 May 2015 14:50:16 +0000 (16:50 +0200)
committerBen Noordhuis <info@bnoordhuis.nl>
Wed, 27 May 2015 19:21:24 +0000 (21:21 +0200)
Replace calls to fs.readFileSync() with an internal variant that does
not create Error objects on failure and is a bit speedier in general.

A secondary benefit is that it improves start-up times in the debugger
because it no longer emits thousands of exception debug events.

On a medium-sized application[0], this commit and its predecessor reduce
start-up times from about 1.5s to 0.5s and reduce the number of start-up
exceptions from ~6100 to 32, half of them internal to the application.

[0] https://github.com/strongloop/loopback-sample-app

PR-URL: https://github.com/nodejs/io.js/pull/1801
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
lib/module.js
src/node_file.cc

index c9e4e2c..65e44b6 100644 (file)
@@ -6,6 +6,7 @@ const runInThisContext = require('vm').runInThisContext;
 const assert = require('assert').ok;
 const fs = require('fs');
 const path = require('path');
+const internalModuleReadFile = process.binding('fs').internalModuleReadFile;
 const internalModuleStat = process.binding('fs').internalModuleStat;
 
 
@@ -65,10 +66,10 @@ function readPackage(requestPath) {
     return packageMainCache[requestPath];
   }
 
-  try {
-    var jsonPath = path.resolve(requestPath, 'package.json');
-    var json = fs.readFileSync(jsonPath, 'utf8');
-  } catch (e) {
+  var jsonPath = path.resolve(requestPath, 'package.json');
+  var json = internalModuleReadFile(jsonPath);
+
+  if (json === undefined) {
     return false;
   }
 
index 4e00f15..c8696f1 100644 (file)
@@ -22,6 +22,8 @@
 # include <io.h>
 #endif
 
+#include <vector>
+
 namespace node {
 
 using v8::Array;
@@ -433,6 +435,50 @@ Local<Value> BuildStatsObject(Environment* env, const uv_stat_t* s) {
   return handle_scope.Escape(stats);
 }
 
+// Used to speed up module loading.  Returns the contents of the file as
+// a string or undefined when the file cannot be opened.  The speedup
+// comes from not creating Error objects on failure.
+static void InternalModuleReadFile(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+
+  CHECK(args[0]->IsString());
+  node::Utf8Value path(env->isolate(), args[0]);
+
+  FILE* const stream = fopen(*path, "rb");
+  if (stream == nullptr) {
+    return;
+  }
+
+  std::vector<char> chars;
+  while (!ferror(stream)) {
+    const size_t kBlockSize = 32 << 10;
+    const size_t start = chars.size();
+    chars.resize(start + kBlockSize);
+    const size_t numchars = fread(&chars[start], 1, kBlockSize, stream);
+    if (numchars < kBlockSize) {
+      chars.resize(start + numchars);
+    }
+    if (numchars == 0) {
+      break;
+    }
+  }
+
+  CHECK_EQ(false, ferror(stream));
+  CHECK_EQ(0, fclose(stream));
+
+  size_t start = 0;
+  if (chars.size() >= 3 && 0 == memcmp(&chars[0], "\xEF\xBB\xBF", 3)) {
+    start = 3;  // Skip UTF-8 BOM.
+  }
+
+  Local<String> chars_string =
+      String::NewFromUtf8(env->isolate(),
+                          &chars[start],
+                          String::kNormalString,
+                          chars.size() - start);
+  args.GetReturnValue().Set(chars_string);
+}
+
 // 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.
@@ -1161,6 +1207,7 @@ void InitFs(Handle<Object> target,
   env->SetMethod(target, "rmdir", RMDir);
   env->SetMethod(target, "mkdir", MKDir);
   env->SetMethod(target, "readdir", ReadDir);
+  env->SetMethod(target, "internalModuleReadFile", InternalModuleReadFile);
   env->SetMethod(target, "internalModuleStat", InternalModuleStat);
   env->SetMethod(target, "stat", Stat);
   env->SetMethod(target, "lstat", LStat);