Skip to content

Commit

Permalink
node: improve performance of hrtime()
Browse files Browse the repository at this point in the history
process.hrtime() was performing too many operations in C++ that could be
done faster in JS. Move those operations over by creating a length 4
Uint32Array and perform bitwise operations on the seconds so that it was
unnecessary for the native API to do any object creation or set any
fields.

This has improved performance from ~350 ns/op to ~65 ns/op. Light
benchmark included to demonstrate the performance change.

PR-URL: nodejs#3780
Reviewed-By: Fedor Indutny <fedor@indutny.com>
  • Loading branch information
trevnorris authored and Fishrock123 committed Jan 6, 2016
1 parent c8fc217 commit 89f056b
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 12 deletions.
18 changes: 18 additions & 0 deletions benchmark/misc/bench-hrtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

const common = require('../common');

const bench = common.createBenchmark(main, {
n: [1e6]
});


function main(conf) {
const n = conf.n >>> 0;

bench.start();
for (var i = 0; i < n; i++) {
process.hrtime();
}
bench.end(n);
}
25 changes: 13 additions & 12 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2132,22 +2132,23 @@ void Hrtime(const FunctionCallbackInfo<Value>& args) {

uint64_t t = uv_hrtime();

if (args.Length() > 0) {
// return a time diff tuple
if (!args[0]->IsArray()) {
if (!args[1]->IsUndefined()) {
if (!args[1]->IsArray()) {
return env->ThrowTypeError(
"process.hrtime() only accepts an Array tuple.");
"process.hrtime() only accepts an Array tuple");
}
Local<Array> inArray = Local<Array>::Cast(args[0]);
uint64_t seconds = inArray->Get(0)->Uint32Value();
uint64_t nanos = inArray->Get(1)->Uint32Value();
t -= (seconds * NANOS_PER_SEC) + nanos;
args.GetReturnValue().Set(true);
}

Local<Array> tuple = Array::New(env->isolate(), 2);
tuple->Set(0, Integer::NewFromUnsigned(env->isolate(), t / NANOS_PER_SEC));
tuple->Set(1, Integer::NewFromUnsigned(env->isolate(), t % NANOS_PER_SEC));
args.GetReturnValue().Set(tuple);
Local<ArrayBuffer> ab = args[0].As<Uint32Array>()->Buffer();
uint32_t* fields = static_cast<uint32_t*>(ab->GetContents().Data());

// These three indices will contain the values for the hrtime tuple. The
// seconds value is broken into the upper/lower 32 bits and stored in two
// uint32 fields to be converted back in JS.
fields[0] = (t / NANOS_PER_SEC) >> 32;
fields[1] = (t / NANOS_PER_SEC) & 0xffffffff;
fields[2] = t % NANOS_PER_SEC;
}

extern "C" void node_module_register(void* m) {
Expand Down
15 changes: 15 additions & 0 deletions src/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,27 @@
}

startup.setupProcessObject = function() {
const _hrtime = process.hrtime;
const hrValues = new Uint32Array(3);

process._setupProcessObject(pushValueToArray);

function pushValueToArray() {
for (var i = 0; i < arguments.length; i++)
this.push(arguments[i]);
}

process.hrtime = function hrtime(ar) {
const ret = [0, 0];
if (_hrtime(hrValues, ar)) {
ret[0] = (hrValues[0] * 0x100000000 + hrValues[1]) - ar[0];
ret[1] = hrValues[2] - ar[1];
} else {
ret[0] = hrValues[0] * 0x100000000 + hrValues[1];
ret[1] = hrValues[2];
}
return ret;
};
};

startup.globalVariables = function() {
Expand Down

0 comments on commit 89f056b

Please sign in to comment.