Skip to content

Commit e9ce8fc

Browse files
euoiatrevnorris
authored andcommitted
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>
1 parent 5d2aef1 commit e9ce8fc

File tree

4 files changed

+100
-40
lines changed

4 files changed

+100
-40
lines changed

lib/fs.js

+32-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,38 @@ function nullCheck(path, callback) {
117117
return true;
118118
}
119119

120-
fs.Stats = binding.Stats;
120+
// Static method to set the stats properties on a Stats object.
121+
fs.Stats = function(
122+
dev,
123+
mode,
124+
nlink,
125+
uid,
126+
gid,
127+
rdev,
128+
ino,
129+
size,
130+
blocks,
131+
atim_msec,
132+
mtim_msec,
133+
ctim_msec,
134+
birthtim_msec) {
135+
this.dev = dev;
136+
this.mode = mode;
137+
this.nlink = nlink;
138+
this.uid = uid;
139+
this.gid = gid;
140+
this.rdev = rdev;
141+
this.ino = ino;
142+
this.size = size;
143+
this.blocks = blocks;
144+
this.atime = new Date(atim_msec);
145+
this.mtime = new Date(mtim_msec);
146+
this.ctime = new Date(ctim_msec);
147+
this.birthtime = new Date(birthtim_msec);
148+
};
149+
150+
// Create a C++ binding to the function which creates a Stats object.
151+
binding.FSInitialize(fs.Stats);
121152

122153
fs.Stats.prototype._checkModeProperty = function(property) {
123154
return ((this.mode & constants.S_IFMT) === property);

src/env.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,14 @@ namespace node {
243243
V(buffer_constructor_function, v8::Function) \
244244
V(context, v8::Context) \
245245
V(domain_array, v8::Array) \
246+
V(fs_stats_constructor_function, v8::Function) \
246247
V(gc_info_callback_function, v8::Function) \
247248
V(module_load_list_array, v8::Array) \
248249
V(pipe_constructor_template, v8::FunctionTemplate) \
249250
V(process_object, v8::Object) \
250251
V(script_context_constructor_template, v8::FunctionTemplate) \
251252
V(script_data_constructor_function, v8::Function) \
252253
V(secure_context_constructor_template, v8::FunctionTemplate) \
253-
V(stats_constructor_function, v8::Function) \
254254
V(tcp_constructor_template, v8::FunctionTemplate) \
255255
V(tick_callback_function, v8::Function) \
256256
V(tls_wrap_constructor_function, v8::Function) \

src/node_file.cc

+66-37
Original file line numberDiff line numberDiff line change
@@ -323,17 +323,12 @@ static void Close(const FunctionCallbackInfo<Value>& args) {
323323
}
324324

325325

326-
Local<Object> BuildStatsObject(Environment* env, const uv_stat_t* s) {
326+
Local<Value> BuildStatsObject(Environment* env, const uv_stat_t* s) {
327327
// If you hit this assertion, you forgot to enter the v8::Context first.
328328
assert(env->context() == env->isolate()->GetCurrentContext());
329329

330330
EscapableHandleScope handle_scope(env->isolate());
331331

332-
Local<Object> stats = env->stats_constructor_function()->NewInstance();
333-
if (stats.IsEmpty()) {
334-
return handle_scope.Escape(Local<Object>());
335-
}
336-
337332
// The code below is very nasty-looking but it prevents a segmentation fault
338333
// when people run JS code like the snippet below. It's apparently more
339334
// 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) {
345340
//
346341
// We need to check the return value of Integer::New() and Date::New()
347342
// and make sure that we bail out when V8 returns an empty handle.
343+
344+
// Integers.
348345
#define X(name) \
349-
{ \
350-
Local<Value> val = Integer::New(env->isolate(), s->st_##name); \
351-
if (val.IsEmpty()) \
352-
return handle_scope.Escape(Local<Object>()); \
353-
stats->Set(env->name ## _string(), val); \
354-
}
346+
Local<Value> name = Integer::New(env->isolate(), s->st_##name); \
347+
if (name.IsEmpty()) \
348+
return handle_scope.Escape(Local<Object>()); \
349+
355350
X(dev)
356351
X(mode)
357352
X(nlink)
@@ -360,39 +355,67 @@ Local<Object> BuildStatsObject(Environment* env, const uv_stat_t* s) {
360355
X(rdev)
361356
# if defined(__POSIX__)
362357
X(blksize)
358+
# else
359+
Local<Value> blksize = Undefined(env->isolate());
363360
# endif
364361
#undef X
365362

363+
// Numbers.
366364
#define X(name) \
367-
{ \
368-
Local<Value> val = Number::New(env->isolate(), \
369-
static_cast<double>(s->st_##name)); \
370-
if (val.IsEmpty()) \
371-
return handle_scope.Escape(Local<Object>()); \
372-
stats->Set(env->name ## _string(), val); \
373-
}
365+
Local<Value> name = Number::New(env->isolate(), \
366+
static_cast<double>(s->st_##name)); \
367+
if (name.IsEmpty()) \
368+
return handle_scope.Escape(Local<Object>()); \
369+
374370
X(ino)
375371
X(size)
376372
# if defined(__POSIX__)
377373
X(blocks)
374+
# else
375+
Local<Value> blocks = Undefined(env->isolate());
378376
# endif
379377
#undef X
380378

381-
#define X(name, rec) \
382-
{ \
383-
double msecs = static_cast<double>(s->st_##rec.tv_sec) * 1000; \
384-
msecs += static_cast<double>(s->st_##rec.tv_nsec / 1000000); \
385-
Local<Value> val = v8::Date::New(env->isolate(), msecs); \
386-
if (val.IsEmpty()) \
387-
return handle_scope.Escape(Local<Object>()); \
388-
stats->Set(env->name ## _string(), val); \
389-
}
390-
X(atime, atim)
391-
X(mtime, mtim)
392-
X(ctime, ctim)
393-
X(birthtime, birthtim)
379+
// Dates.
380+
#define X(name) \
381+
Local<Value> name##_msec = \
382+
Number::New(env->isolate(), \
383+
(static_cast<double>(s->st_##name.tv_sec) * 1000) + \
384+
(static_cast<double>(s->st_##name.tv_nsec / 1000000))); \
385+
\
386+
if (name##_msec.IsEmpty()) \
387+
return handle_scope.Escape(Local<Object>()); \
388+
389+
X(atim)
390+
X(mtim)
391+
X(ctim)
392+
X(birthtim)
394393
#undef X
395394

395+
// Pass stats as the first argument, this is the object we are modifying.
396+
Local<Value> argv[] = {
397+
dev,
398+
mode,
399+
nlink,
400+
uid,
401+
gid,
402+
rdev,
403+
ino,
404+
size,
405+
blocks,
406+
atim_msec,
407+
mtim_msec,
408+
ctim_msec,
409+
birthtim_msec
410+
};
411+
412+
// Call out to JavaScript to create the stats object.
413+
Local<Value> stats =
414+
env->fs_stats_constructor_function()->NewInstance(ARRAY_SIZE(argv), argv);
415+
416+
if (stats.IsEmpty())
417+
return handle_scope.Escape(Local<Object>());
418+
396419
return handle_scope.Escape(stats);
397420
}
398421

@@ -1081,18 +1104,24 @@ static void FUTimes(const FunctionCallbackInfo<Value>& args) {
10811104
}
10821105
}
10831106

1107+
void FSInitialize(const FunctionCallbackInfo<Value>& args) {
1108+
Local<Function> stats_constructor = args[0].As<Function>();
1109+
assert(stats_constructor->IsFunction());
1110+
1111+
Environment* env = Environment::GetCurrent(args.GetIsolate());
1112+
env->set_fs_stats_constructor_function(stats_constructor);
1113+
}
10841114

10851115
void InitFs(Handle<Object> target,
10861116
Handle<Value> unused,
10871117
Handle<Context> context,
10881118
void* priv) {
10891119
Environment* env = Environment::GetCurrent(context);
10901120

1091-
// Initialize the stats object
1092-
Local<Function> constructor =
1093-
FunctionTemplate::New(env->isolate())->GetFunction();
1094-
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "Stats"), constructor);
1095-
env->set_stats_constructor_function(constructor);
1121+
// Function which creates a new Stats object.
1122+
target->Set(
1123+
FIXED_ONE_BYTE_STRING(env->isolate(), "FSInitialize"),
1124+
FunctionTemplate::New(env->isolate(), FSInitialize)->GetFunction());
10961125

10971126
NODE_SET_METHOD(target, "close", Close);
10981127
NODE_SET_METHOD(target, "open", Open);

src/node_internals.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void AppendExceptionLine(Environment* env,
126126

127127
NO_RETURN void FatalError(const char* location, const char* message);
128128

129-
v8::Local<v8::Object> BuildStatsObject(Environment* env, const uv_stat_t* s);
129+
v8::Local<v8::Value> BuildStatsObject(Environment* env, const uv_stat_t* s);
130130

131131
enum Endianness {
132132
kLittleEndian, // _Not_ LITTLE_ENDIAN, clashes with endian.h.

0 commit comments

Comments
 (0)