Browse Source

fs: improve performance of all stat functions

By building the fs.Stats object in JS, which is returned by all fs stat
functions, calls to v8::Object::Set() are removed. This also includes
creating all associated Date objects in JS, rather than using
v8::Date::New(). Both these changes have significant performance gains.

Note that the returned value from fs.stat changes slightly for non-POSIX
systems. Whereas before the stats object would be missing blocks and
blksize keys, it now has these keys with undefined as the value.

Signed-off-by: Trevor Norris <trev.norris@gmail.com>
v0.11.13-release
James Pickard 11 years ago
committed by Trevor Norris
parent
commit
e9ce8fc82a
  1. 33
      lib/fs.js
  2. 2
      src/env.h
  3. 103
      src/node_file.cc
  4. 2
      src/node_internals.h

33
lib/fs.js

@ -117,7 +117,38 @@ function nullCheck(path, callback) {
return true; return true;
} }
fs.Stats = binding.Stats; // Static method to set the stats properties on a Stats object.
fs.Stats = function(
dev,
mode,
nlink,
uid,
gid,
rdev,
ino,
size,
blocks,
atim_msec,
mtim_msec,
ctim_msec,
birthtim_msec) {
this.dev = dev;
this.mode = mode;
this.nlink = nlink;
this.uid = uid;
this.gid = gid;
this.rdev = rdev;
this.ino = ino;
this.size = size;
this.blocks = blocks;
this.atime = new Date(atim_msec);
this.mtime = new Date(mtim_msec);
this.ctime = new Date(ctim_msec);
this.birthtime = new Date(birthtim_msec);
};
// Create a C++ binding to the function which creates a Stats object.
binding.FSInitialize(fs.Stats);
fs.Stats.prototype._checkModeProperty = function(property) { fs.Stats.prototype._checkModeProperty = function(property) {
return ((this.mode & constants.S_IFMT) === property); return ((this.mode & constants.S_IFMT) === property);

2
src/env.h

@ -243,6 +243,7 @@ namespace node {
V(buffer_constructor_function, v8::Function) \ V(buffer_constructor_function, v8::Function) \
V(context, v8::Context) \ V(context, v8::Context) \
V(domain_array, v8::Array) \ V(domain_array, v8::Array) \
V(fs_stats_constructor_function, v8::Function) \
V(gc_info_callback_function, v8::Function) \ V(gc_info_callback_function, v8::Function) \
V(module_load_list_array, v8::Array) \ V(module_load_list_array, v8::Array) \
V(pipe_constructor_template, v8::FunctionTemplate) \ V(pipe_constructor_template, v8::FunctionTemplate) \
@ -250,7 +251,6 @@ namespace node {
V(script_context_constructor_template, v8::FunctionTemplate) \ V(script_context_constructor_template, v8::FunctionTemplate) \
V(script_data_constructor_function, v8::Function) \ V(script_data_constructor_function, v8::Function) \
V(secure_context_constructor_template, v8::FunctionTemplate) \ V(secure_context_constructor_template, v8::FunctionTemplate) \
V(stats_constructor_function, v8::Function) \
V(tcp_constructor_template, v8::FunctionTemplate) \ V(tcp_constructor_template, v8::FunctionTemplate) \
V(tick_callback_function, v8::Function) \ V(tick_callback_function, v8::Function) \
V(tls_wrap_constructor_function, v8::Function) \ V(tls_wrap_constructor_function, v8::Function) \

103
src/node_file.cc

@ -323,17 +323,12 @@ static void Close(const FunctionCallbackInfo<Value>& args) {
} }
Local<Object> BuildStatsObject(Environment* env, const uv_stat_t* s) { Local<Value> BuildStatsObject(Environment* env, const uv_stat_t* s) {
// If you hit this assertion, you forgot to enter the v8::Context first. // If you hit this assertion, you forgot to enter the v8::Context first.
assert(env->context() == env->isolate()->GetCurrentContext()); assert(env->context() == env->isolate()->GetCurrentContext());
EscapableHandleScope handle_scope(env->isolate()); EscapableHandleScope handle_scope(env->isolate());
Local<Object> stats = env->stats_constructor_function()->NewInstance();
if (stats.IsEmpty()) {
return handle_scope.Escape(Local<Object>());
}
// The code below is very nasty-looking but it prevents a segmentation fault // The code below is very nasty-looking but it prevents a segmentation fault
// when people run JS code like the snippet below. It's apparently more // when people run JS code like the snippet below. It's apparently more
// common than you would expect, several people have reported this crash... // common than you would expect, several people have reported this crash...
@ -345,13 +340,13 @@ Local<Object> BuildStatsObject(Environment* env, const uv_stat_t* s) {
// //
// We need to check the return value of Integer::New() and Date::New() // We need to check the return value of Integer::New() and Date::New()
// and make sure that we bail out when V8 returns an empty handle. // and make sure that we bail out when V8 returns an empty handle.
// Integers.
#define X(name) \ #define X(name) \
{ \ Local<Value> name = Integer::New(env->isolate(), s->st_##name); \
Local<Value> val = Integer::New(env->isolate(), s->st_##name); \ if (name.IsEmpty()) \
if (val.IsEmpty()) \ return handle_scope.Escape(Local<Object>()); \
return handle_scope.Escape(Local<Object>()); \
stats->Set(env->name ## _string(), val); \
}
X(dev) X(dev)
X(mode) X(mode)
X(nlink) X(nlink)
@ -360,39 +355,67 @@ Local<Object> BuildStatsObject(Environment* env, const uv_stat_t* s) {
X(rdev) X(rdev)
# if defined(__POSIX__) # if defined(__POSIX__)
X(blksize) X(blksize)
# else
Local<Value> blksize = Undefined(env->isolate());
# endif # endif
#undef X #undef X
// Numbers.
#define X(name) \ #define X(name) \
{ \ Local<Value> name = Number::New(env->isolate(), \
Local<Value> val = Number::New(env->isolate(), \ static_cast<double>(s->st_##name)); \
static_cast<double>(s->st_##name)); \ if (name.IsEmpty()) \
if (val.IsEmpty()) \ return handle_scope.Escape(Local<Object>()); \
return handle_scope.Escape(Local<Object>()); \
stats->Set(env->name ## _string(), val); \
}
X(ino) X(ino)
X(size) X(size)
# if defined(__POSIX__) # if defined(__POSIX__)
X(blocks) X(blocks)
# else
Local<Value> blocks = Undefined(env->isolate());
# endif # endif
#undef X #undef X
#define X(name, rec) \ // Dates.
{ \ #define X(name) \
double msecs = static_cast<double>(s->st_##rec.tv_sec) * 1000; \ Local<Value> name##_msec = \
msecs += static_cast<double>(s->st_##rec.tv_nsec / 1000000); \ Number::New(env->isolate(), \
Local<Value> val = v8::Date::New(env->isolate(), msecs); \ (static_cast<double>(s->st_##name.tv_sec) * 1000) + \
if (val.IsEmpty()) \ (static_cast<double>(s->st_##name.tv_nsec / 1000000))); \
return handle_scope.Escape(Local<Object>()); \ \
stats->Set(env->name ## _string(), val); \ if (name##_msec.IsEmpty()) \
} return handle_scope.Escape(Local<Object>()); \
X(atime, atim)
X(mtime, mtim) X(atim)
X(ctime, ctim) X(mtim)
X(birthtime, birthtim) X(ctim)
X(birthtim)
#undef X #undef X
// Pass stats as the first argument, this is the object we are modifying.
Local<Value> argv[] = {
dev,
mode,
nlink,
uid,
gid,
rdev,
ino,
size,
blocks,
atim_msec,
mtim_msec,
ctim_msec,
birthtim_msec
};
// Call out to JavaScript to create the stats object.
Local<Value> stats =
env->fs_stats_constructor_function()->NewInstance(ARRAY_SIZE(argv), argv);
if (stats.IsEmpty())
return handle_scope.Escape(Local<Object>());
return handle_scope.Escape(stats); return handle_scope.Escape(stats);
} }
@ -1081,6 +1104,13 @@ static void FUTimes(const FunctionCallbackInfo<Value>& args) {
} }
} }
void FSInitialize(const FunctionCallbackInfo<Value>& args) {
Local<Function> stats_constructor = args[0].As<Function>();
assert(stats_constructor->IsFunction());
Environment* env = Environment::GetCurrent(args.GetIsolate());
env->set_fs_stats_constructor_function(stats_constructor);
}
void InitFs(Handle<Object> target, void InitFs(Handle<Object> target,
Handle<Value> unused, Handle<Value> unused,
@ -1088,11 +1118,10 @@ void InitFs(Handle<Object> target,
void* priv) { void* priv) {
Environment* env = Environment::GetCurrent(context); Environment* env = Environment::GetCurrent(context);
// Initialize the stats object // Function which creates a new Stats object.
Local<Function> constructor = target->Set(
FunctionTemplate::New(env->isolate())->GetFunction(); FIXED_ONE_BYTE_STRING(env->isolate(), "FSInitialize"),
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Stats"), constructor); FunctionTemplate::New(env->isolate(), FSInitialize)->GetFunction());
env->set_stats_constructor_function(constructor);
NODE_SET_METHOD(target, "close", Close); NODE_SET_METHOD(target, "close", Close);
NODE_SET_METHOD(target, "open", Open); NODE_SET_METHOD(target, "open", Open);

2
src/node_internals.h

@ -126,7 +126,7 @@ void AppendExceptionLine(Environment* env,
NO_RETURN void FatalError(const char* location, const char* message); NO_RETURN void FatalError(const char* location, const char* message);
v8::Local<v8::Object> BuildStatsObject(Environment* env, const uv_stat_t* s); v8::Local<v8::Value> BuildStatsObject(Environment* env, const uv_stat_t* s);
enum Endianness { enum Endianness {
kLittleEndian, // _Not_ LITTLE_ENDIAN, clashes with endian.h. kLittleEndian, // _Not_ LITTLE_ENDIAN, clashes with endian.h.

Loading…
Cancel
Save