fs: improve performance for sync stat() functions
PR-URL: https://github.com/nodejs/node/pull/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>
This commit is contained in:
parent
efdc571a58
commit
22abb39b2c
@ -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
lib/fs.js
26
lib/fs.js
@ -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,
|
||||
@ -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);
|
||||
@ -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;
|
||||
@ -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) {
|
||||
|
@ -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;
|
||||
@ -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.
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user