From e29095ac9fc17b34ea7d9bf02d8770653b007290 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 26 May 2015 16:50:16 +0200 Subject: [PATCH] lib: speed up require(), phase 2 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 --- lib/module.js | 9 +++++---- src/node_file.cc | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/lib/module.js b/lib/module.js index c9e4e2c78..65e44b60b 100644 --- a/lib/module.js +++ b/lib/module.js @@ -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; } diff --git a/src/node_file.cc b/src/node_file.cc index 4e00f15e2..c8696f129 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -22,6 +22,8 @@ # include #endif +#include + namespace node { using v8::Array; @@ -433,6 +435,50 @@ Local 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& 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 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 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 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);