Skip to content

Commit c8d2ca7

Browse files
mscdexMylesBorins
authored andcommitted
fs: improve performance for sync stat() functions
PR-URL: #11522 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent 5bfa43d commit c8d2ca7

3 files changed

Lines changed: 102 additions & 28 deletions

File tree

‎benchmark/fs/bench-statSync.js‎

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,35 @@ const fs = require('fs');
55

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

1111

1212
functionmain(conf){
1313
constn=conf.n>>>0;
14-
constfn=fs[conf.kind];
15-
16-
bench.start();
17-
for(vari=0;i<n;i++){
18-
fn(__filename);
14+
varfn;
15+
vari;
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+
constfd=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+
thrownewError('Invalid kind argument');
1938
}
20-
bench.end(n);
2139
}

‎lib/fs.js‎

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ function isFd(path) {
147147
}
148148

149149
// Static method to set the stats properties on a Stats object.
150-
fs.Stats=function(
150+
functionStats(
151151
dev,
152152
mode,
153153
nlink,
@@ -176,7 +176,8 @@ fs.Stats = function(
176176
this.mtime=newDate(mtim_msec);
177177
this.ctime=newDate(ctim_msec);
178178
this.birthtime=newDate(birthtim_msec);
179-
};
179+
}
180+
fs.Stats=Stats;
180181

181182
// Create a C++ binding to the function which creates a Stats object.
182183
binding.FSInitialize(fs.Stats);
@@ -261,7 +262,7 @@ fs.exists = function(path, callback) {
261262
fs.existsSync=function(path){
262263
try{
263264
nullCheck(path);
264-
binding.stat(pathModule._makeLong(path));
265+
binding.stat(pathModule._makeLong(path),statValues);
265266
returntrue;
266267
}catch(e){
267268
returnfalse;
@@ -973,18 +974,31 @@ fs.stat = function(path, callback) {
973974
binding.stat(pathModule._makeLong(path),req);
974975
};
975976

977+
conststatValues=newFloat64Array(14);
978+
functionstatsFromValues(){
979+
returnnewStats(statValues[0],statValues[1],statValues[2],statValues[3],
980+
statValues[4],statValues[5],
981+
statValues[6]<0 ? undefined : statValues[6],statValues[7],
982+
statValues[8],statValues[9]<0 ? undefined : statValues[9],
983+
statValues[10],statValues[11],statValues[12],
984+
statValues[13]);
985+
}
986+
976987
fs.fstatSync=function(fd){
977-
returnbinding.fstat(fd);
988+
binding.fstat(fd,statValues);
989+
returnstatsFromValues();
978990
};
979991

980992
fs.lstatSync=function(path){
981993
nullCheck(path);
982-
returnbinding.lstat(pathModule._makeLong(path));
994+
binding.lstat(pathModule._makeLong(path),statValues);
995+
returnstatsFromValues();
983996
};
984997

985998
fs.statSync=function(path){
986999
nullCheck(path);
987-
returnbinding.stat(pathModule._makeLong(path));
1000+
binding.stat(pathModule._makeLong(path),statValues);
1001+
returnstatsFromValues();
9881002
};
9891003

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

‎src/node_file.cc‎

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@
2727
namespacenode {
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+
voidFillStatsArray(double* fields, constuv_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+
#defineX(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.
@@ -618,12 +651,15 @@ static void Stat(const FunctionCallbackInfo<Value>& args) {
618651
BufferValue path(env->isolate(), args[0]);
619652
ASSERT_PATH(path)
620653

621-
if (args[1]->IsObject()) {
622-
ASYNC_CALL(stat, args[1], UTF8, *path)
623-
} else {
654+
if (args[1]->IsFloat64Array()) {
655+
Local<Float64Array> array = args[1].As<Float64Array>();
656+
CHECK_EQ(array->Length(), 14);
657+
Local<ArrayBuffer> ab = array->Buffer();
658+
double* fields = static_cast<double*>(ab->GetContents().Data());
624659
SYNC_CALL(stat, *path, *path)
625-
args.GetReturnValue().Set(
626-
BuildStatsObject(env, static_cast<constuv_stat_t*>(SYNC_REQ.ptr)));
660+
FillStatsArray(fields, static_cast<constuv_stat_t*>(SYNC_REQ.ptr));
661+
} elseif (args[1]->IsObject()) {
662+
ASYNC_CALL(stat, args[1], UTF8, *path)
627663
}
628664
}
629665

@@ -636,12 +672,15 @@ static void LStat(const FunctionCallbackInfo<Value>& args) {
636672
BufferValue path(env->isolate(), args[0]);
637673
ASSERT_PATH(path)
638674

639-
if (args[1]->IsObject()) {
640-
ASYNC_CALL(lstat, args[1], UTF8, *path)
641-
} else {
675+
if (args[1]->IsFloat64Array()) {
676+
Local<Float64Array> array = args[1].As<Float64Array>();
677+
CHECK_EQ(array->Length(), 14);
678+
Local<ArrayBuffer> ab = array->Buffer();
679+
double* fields = static_cast<double*>(ab->GetContents().Data());
642680
SYNC_CALL(lstat, *path, *path)
643-
args.GetReturnValue().Set(
644-
BuildStatsObject(env, static_cast<constuv_stat_t*>(SYNC_REQ.ptr)));
681+
FillStatsArray(fields, static_cast<constuv_stat_t*>(SYNC_REQ.ptr));
682+
} elseif (args[1]->IsObject()) {
683+
ASYNC_CALL(lstat, args[1], UTF8, *path)
645684
}
646685
}
647686

@@ -655,12 +694,15 @@ static void FStat(const FunctionCallbackInfo<Value>& args) {
655694

656695
int fd = args[0]->Int32Value();
657696

658-
if (args[1]->IsObject()) {
659-
ASYNC_CALL(fstat, args[1], UTF8, fd)
660-
} else {
697+
if (args[1]->IsFloat64Array()) {
698+
Local<Float64Array> array = args[1].As<Float64Array>();
699+
CHECK_EQ(array->Length(), 14);
700+
Local<ArrayBuffer> ab = array->Buffer();
701+
double* fields = static_cast<double*>(ab->GetContents().Data());
661702
SYNC_CALL(fstat, 0, fd)
662-
args.GetReturnValue().Set(
663-
BuildStatsObject(env, static_cast<constuv_stat_t*>(SYNC_REQ.ptr)));
703+
FillStatsArray(fields, static_cast<constuv_stat_t*>(SYNC_REQ.ptr));
704+
} elseif (args[1]->IsObject()) {
705+
ASYNC_CALL(fstat, args[1], UTF8, fd)
664706
}
665707
}
666708

0 commit comments

Comments
 (0)