Skip to content

Commit 7581263

Browse files
mscdexitaloacasas
authored andcommitted
fs: improve performance for sync stat() functions
PR-URL: #11522 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent db06c73 commit 7581263

File tree

3 files changed

+102
-28
lines changed

3 files changed

+102
-28
lines changed

benchmark/fs/bench-statSync.js

+25-7
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,35 @@ const fs = require('fs');
55

66
const bench = common.createBenchmark(main, {
77
n: [1e4],
8-
kind: ['lstatSync', 'statSync']
8+
kind: ['fstatSync', 'lstatSync', 'statSync']
99
});
1010

1111

1212
function main(conf) {
1313
const n = conf.n >>> 0;
14-
const fn = fs[conf.kind];
15-
16-
bench.start();
17-
for (var i = 0; i < n; i++) {
18-
fn(__filename);
14+
var fn;
15+
var i;
16+
switch (conf.kind) {
17+
case 'statSync':
18+
case 'lstatSync':
19+
fn = fs[conf.kind];
20+
bench.start();
21+
for (i = 0; i < n; i++) {
22+
fn(__filename);
23+
}
24+
bench.end(n);
25+
break;
26+
case 'fstatSync':
27+
fn = fs.fstatSync;
28+
const fd = fs.openSync(__filename, 'r');
29+
bench.start();
30+
for (i = 0; i < n; i++) {
31+
fn(fd);
32+
}
33+
bench.end(n);
34+
fs.closeSync(fd);
35+
break;
36+
default:
37+
throw new Error('Invalid kind argument');
1938
}
20-
bench.end(n);
2139
}

lib/fs.js

+20-6
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ function isFd(path) {
132132
}
133133

134134
// Static method to set the stats properties on a Stats object.
135-
fs.Stats = function(
135+
function Stats(
136136
dev,
137137
mode,
138138
nlink,
@@ -161,7 +161,8 @@ fs.Stats = function(
161161
this.mtime = new Date(mtim_msec);
162162
this.ctime = new Date(ctim_msec);
163163
this.birthtime = new Date(birthtim_msec);
164-
};
164+
}
165+
fs.Stats = Stats;
165166

166167
// Create a C++ binding to the function which creates a Stats object.
167168
binding.FSInitialize(fs.Stats);
@@ -263,7 +264,7 @@ fs.existsSync = function(path) {
263264
try {
264265
handleError((path = getPathFromURL(path)));
265266
nullCheck(path);
266-
binding.stat(pathModule._makeLong(path));
267+
binding.stat(pathModule._makeLong(path), statValues);
267268
return true;
268269
} catch (e) {
269270
return false;
@@ -938,20 +939,33 @@ fs.stat = function(path, callback) {
938939
binding.stat(pathModule._makeLong(path), req);
939940
};
940941

942+
const statValues = new Float64Array(14);
943+
function statsFromValues() {
944+
return new Stats(statValues[0], statValues[1], statValues[2], statValues[3],
945+
statValues[4], statValues[5],
946+
statValues[6] < 0 ? undefined : statValues[6], statValues[7],
947+
statValues[8], statValues[9] < 0 ? undefined : statValues[9],
948+
statValues[10], statValues[11], statValues[12],
949+
statValues[13]);
950+
}
951+
941952
fs.fstatSync = function(fd) {
942-
return binding.fstat(fd);
953+
binding.fstat(fd, statValues);
954+
return statsFromValues();
943955
};
944956

945957
fs.lstatSync = function(path) {
946958
handleError((path = getPathFromURL(path)));
947959
nullCheck(path);
948-
return binding.lstat(pathModule._makeLong(path));
960+
binding.lstat(pathModule._makeLong(path), statValues);
961+
return statsFromValues();
949962
};
950963

951964
fs.statSync = function(path) {
952965
handleError((path = getPathFromURL(path)));
953966
nullCheck(path);
954-
return binding.stat(pathModule._makeLong(path));
967+
binding.stat(pathModule._makeLong(path), statValues);
968+
return statsFromValues();
955969
};
956970

957971
fs.readlink = function(path, options, callback) {

src/node_file.cc

+57-15
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@
2727
namespace node {
2828

2929
using v8::Array;
30+
using v8::ArrayBuffer;
3031
using v8::Context;
3132
using v8::EscapableHandleScope;
33+
using v8::Float64Array;
3234
using v8::Function;
3335
using v8::FunctionCallbackInfo;
3436
using v8::FunctionTemplate;
@@ -528,6 +530,37 @@ Local<Value> BuildStatsObject(Environment* env, const uv_stat_t* s) {
528530
return handle_scope.Escape(stats);
529531
}
530532

533+
void FillStatsArray(double* fields, const uv_stat_t* s) {
534+
fields[0] = s->st_dev;
535+
fields[1] = s->st_mode;
536+
fields[2] = s->st_nlink;
537+
fields[3] = s->st_uid;
538+
fields[4] = s->st_gid;
539+
fields[5] = s->st_rdev;
540+
#if defined(__POSIX__)
541+
fields[6] = s->st_blksize;
542+
#else
543+
fields[6] = -1;
544+
#endif
545+
fields[7] = s->st_ino;
546+
fields[8] = s->st_size;
547+
#if defined(__POSIX__)
548+
fields[9] = s->st_blocks;
549+
#else
550+
fields[9] = -1;
551+
#endif
552+
// Dates.
553+
#define X(idx, name) \
554+
fields[idx] = (static_cast<double>(s->st_##name.tv_sec) * 1000) + \
555+
(static_cast<double>(s->st_##name.tv_nsec / 1000000)); \
556+
557+
X(10, atim)
558+
X(11, mtim)
559+
X(12, ctim)
560+
X(13, birthtim)
561+
#undef X
562+
}
563+
531564
// Used to speed up module loading. Returns the contents of the file as
532565
// a string or undefined when the file cannot be opened. The speedup
533566
// comes from not creating Error objects on failure.
@@ -612,12 +645,15 @@ static void Stat(const FunctionCallbackInfo<Value>& args) {
612645
BufferValue path(env->isolate(), args[0]);
613646
ASSERT_PATH(path)
614647

615-
if (args[1]->IsObject()) {
616-
ASYNC_CALL(stat, args[1], UTF8, *path)
617-
} else {
648+
if (args[1]->IsFloat64Array()) {
649+
Local<Float64Array> array = args[1].As<Float64Array>();
650+
CHECK_EQ(array->Length(), 14);
651+
Local<ArrayBuffer> ab = array->Buffer();
652+
double* fields = static_cast<double*>(ab->GetContents().Data());
618653
SYNC_CALL(stat, *path, *path)
619-
args.GetReturnValue().Set(
620-
BuildStatsObject(env, static_cast<const uv_stat_t*>(SYNC_REQ.ptr)));
654+
FillStatsArray(fields, static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
655+
} else if (args[1]->IsObject()) {
656+
ASYNC_CALL(stat, args[1], UTF8, *path)
621657
}
622658
}
623659

@@ -630,12 +666,15 @@ static void LStat(const FunctionCallbackInfo<Value>& args) {
630666
BufferValue path(env->isolate(), args[0]);
631667
ASSERT_PATH(path)
632668

633-
if (args[1]->IsObject()) {
634-
ASYNC_CALL(lstat, args[1], UTF8, *path)
635-
} else {
669+
if (args[1]->IsFloat64Array()) {
670+
Local<Float64Array> array = args[1].As<Float64Array>();
671+
CHECK_EQ(array->Length(), 14);
672+
Local<ArrayBuffer> ab = array->Buffer();
673+
double* fields = static_cast<double*>(ab->GetContents().Data());
636674
SYNC_CALL(lstat, *path, *path)
637-
args.GetReturnValue().Set(
638-
BuildStatsObject(env, static_cast<const uv_stat_t*>(SYNC_REQ.ptr)));
675+
FillStatsArray(fields, static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
676+
} else if (args[1]->IsObject()) {
677+
ASYNC_CALL(lstat, args[1], UTF8, *path)
639678
}
640679
}
641680

@@ -649,12 +688,15 @@ static void FStat(const FunctionCallbackInfo<Value>& args) {
649688

650689
int fd = args[0]->Int32Value();
651690

652-
if (args[1]->IsObject()) {
653-
ASYNC_CALL(fstat, args[1], UTF8, fd)
654-
} else {
691+
if (args[1]->IsFloat64Array()) {
692+
Local<Float64Array> array = args[1].As<Float64Array>();
693+
CHECK_EQ(array->Length(), 14);
694+
Local<ArrayBuffer> ab = array->Buffer();
695+
double* fields = static_cast<double*>(ab->GetContents().Data());
655696
SYNC_CALL(fstat, 0, fd)
656-
args.GetReturnValue().Set(
657-
BuildStatsObject(env, static_cast<const uv_stat_t*>(SYNC_REQ.ptr)));
697+
FillStatsArray(fields, static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
698+
} else if (args[1]->IsObject()) {
699+
ASYNC_CALL(fstat, args[1], UTF8, fd)
658700
}
659701
}
660702

0 commit comments

Comments
 (0)