Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs: improve performance for sync stat() functions #11522

Merged
merged 1 commit into from
Feb 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions benchmark/fs/bench-statSync.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,35 @@ const fs = require('fs');

const bench = common.createBenchmark(main, {
n: [1e4],
kind: ['lstatSync', 'statSync']
kind: ['fstatSync', 'lstatSync', 'statSync']
});


function main(conf) {
const n = conf.n >>> 0;
const fn = fs[conf.kind];

bench.start();
for (var i = 0; i < n; i++) {
fn(__filename);
var fn;
var i;
switch (conf.kind) {
case 'statSync':
case 'lstatSync':
fn = fs[conf.kind];
bench.start();
for (i = 0; i < n; i++) {
fn(__filename);
}
bench.end(n);
break;
case 'fstatSync':
fn = fs.fstatSync;
const fd = fs.openSync(__filename, 'r');
bench.start();
for (i = 0; i < n; i++) {
fn(fd);
}
bench.end(n);
fs.closeSync(fd);
break;
default:
throw new Error('Invalid kind argument');
}
bench.end(n);
}
26 changes: 20 additions & 6 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ function isFd(path) {
}

// Static method to set the stats properties on a Stats object.
fs.Stats = function(
function Stats(
dev,
mode,
nlink,
Expand Down Expand Up @@ -160,7 +160,8 @@ fs.Stats = function(
this.mtime = new Date(mtim_msec);
this.ctime = new Date(ctim_msec);
this.birthtime = new Date(birthtim_msec);
};
}
fs.Stats = Stats;

// Create a C++ binding to the function which creates a Stats object.
binding.FSInitialize(fs.Stats);
Expand Down Expand Up @@ -262,7 +263,7 @@ fs.existsSync = function(path) {
try {
handleError((path = getPathFromURL(path)));
nullCheck(path);
binding.stat(pathModule._makeLong(path));
binding.stat(pathModule._makeLong(path), statValues);
return true;
} catch (e) {
return false;
Expand Down Expand Up @@ -856,20 +857,33 @@ fs.stat = function(path, callback) {
binding.stat(pathModule._makeLong(path), req);
};

const statValues = new Float64Array(14);
function statsFromValues() {
return new Stats(statValues[0], statValues[1], statValues[2], statValues[3],
statValues[4], statValues[5],
statValues[6] < 0 ? undefined : statValues[6], statValues[7],
statValues[8], statValues[9] < 0 ? undefined : statValues[9],
statValues[10], statValues[11], statValues[12],
statValues[13]);
}

fs.fstatSync = function(fd) {
return binding.fstat(fd);
binding.fstat(fd, statValues);
return statsFromValues();
};

fs.lstatSync = function(path) {
handleError((path = getPathFromURL(path)));
nullCheck(path);
return binding.lstat(pathModule._makeLong(path));
binding.lstat(pathModule._makeLong(path), statValues);
return statsFromValues();
};

fs.statSync = function(path) {
handleError((path = getPathFromURL(path)));
nullCheck(path);
return binding.stat(pathModule._makeLong(path));
binding.stat(pathModule._makeLong(path), statValues);
return statsFromValues();
};

fs.readlink = function(path, options, callback) {
Expand Down
72 changes: 57 additions & 15 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
namespace node {

using v8::Array;
using v8::ArrayBuffer;
using v8::Context;
using v8::EscapableHandleScope;
using v8::Float64Array;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
Expand Down Expand Up @@ -528,6 +530,37 @@ Local<Value> BuildStatsObject(Environment* env, const uv_stat_t* s) {
return handle_scope.Escape(stats);
}

void FillStatsArray(double* fields, const uv_stat_t* s) {
fields[0] = s->st_dev;
fields[1] = s->st_mode;
fields[2] = s->st_nlink;
fields[3] = s->st_uid;
fields[4] = s->st_gid;
fields[5] = s->st_rdev;
#if defined(__POSIX__)
fields[6] = s->st_blksize;
#else
fields[6] = -1;
#endif
fields[7] = s->st_ino;
fields[8] = s->st_size;
#if defined(__POSIX__)
fields[9] = s->st_blocks;
#else
fields[9] = -1;
#endif
// Dates.
#define X(idx, name) \
fields[idx] = (static_cast<double>(s->st_##name.tv_sec) * 1000) + \
(static_cast<double>(s->st_##name.tv_nsec / 1000000)); \

X(10, atim)
X(11, mtim)
X(12, ctim)
X(13, birthtim)
#undef X
}

// 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.
Expand Down Expand Up @@ -612,12 +645,15 @@ static void Stat(const FunctionCallbackInfo<Value>& args) {
BufferValue path(env->isolate(), args[0]);
ASSERT_PATH(path)

if (args[1]->IsObject()) {
ASYNC_CALL(stat, args[1], UTF8, *path)
} else {
if (args[1]->IsFloat64Array()) {
Local<Float64Array> array = args[1].As<Float64Array>();
CHECK_EQ(array->Length(), 14);
Local<ArrayBuffer> ab = array->Buffer();
double* fields = static_cast<double*>(ab->GetContents().Data());
SYNC_CALL(stat, *path, *path)
args.GetReturnValue().Set(
BuildStatsObject(env, static_cast<const uv_stat_t*>(SYNC_REQ.ptr)));
FillStatsArray(fields, static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
} else if (args[1]->IsObject()) {
ASYNC_CALL(stat, args[1], UTF8, *path)
}
}

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

if (args[1]->IsObject()) {
ASYNC_CALL(lstat, args[1], UTF8, *path)
} else {
if (args[1]->IsFloat64Array()) {
Local<Float64Array> array = args[1].As<Float64Array>();
CHECK_EQ(array->Length(), 14);
Local<ArrayBuffer> ab = array->Buffer();
double* fields = static_cast<double*>(ab->GetContents().Data());
SYNC_CALL(lstat, *path, *path)
args.GetReturnValue().Set(
BuildStatsObject(env, static_cast<const uv_stat_t*>(SYNC_REQ.ptr)));
FillStatsArray(fields, static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
} else if (args[1]->IsObject()) {
ASYNC_CALL(lstat, args[1], UTF8, *path)
}
}

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

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

if (args[1]->IsObject()) {
ASYNC_CALL(fstat, args[1], UTF8, fd)
} else {
if (args[1]->IsFloat64Array()) {
Local<Float64Array> array = args[1].As<Float64Array>();
CHECK_EQ(array->Length(), 14);
Local<ArrayBuffer> ab = array->Buffer();
double* fields = static_cast<double*>(ab->GetContents().Data());
SYNC_CALL(fstat, 0, fd)
args.GetReturnValue().Set(
BuildStatsObject(env, static_cast<const uv_stat_t*>(SYNC_REQ.ptr)));
FillStatsArray(fields, static_cast<const uv_stat_t*>(SYNC_REQ.ptr));
} else if (args[1]->IsObject()) {
ASYNC_CALL(fstat, args[1], UTF8, fd)
}
}

Expand Down