Skip to content

Commit

Permalink
process: improve process.hrtime
Browse files Browse the repository at this point in the history
* Add benchmarks for diffing a previous result
* Improvements to the documentation, including type annotation
* Update the outdated comments in src/node.cc, improve comments
  in lib/internal/process.js
* Check the argument is an Array Tuple with length 2

PR-URL: #10764
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Brian White <mscdex@mscdex.net>
  • Loading branch information
joyeecheung committed Jan 23, 2017
1 parent 9fcd842 commit a647d82
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 38 deletions.
28 changes: 21 additions & 7 deletions benchmark/process/bench-hrtime.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
'use strict';

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

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


function main(conf) {
const n = conf.n >>> 0;
const n = conf.n | 0;
const hrtime = process.hrtime;
var noDead = hrtime();
var i;

bench.start();
for (var i = 0; i < n; i++) {
process.hrtime();
if (conf.type === 'raw') {
bench.start();
for (i = 0; i < n; i++) {
noDead = hrtime();
}
bench.end(n);
} else {
bench.start();
for (i = 0; i < n; i++) {
noDead = hrtime(noDead);
}
bench.end(n);
}
bench.end(n);

assert.ok(Array.isArray(noDead));
}
30 changes: 17 additions & 13 deletions doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -1016,33 +1016,37 @@ Android)
added: v0.7.6
-->

The `process.hrtime()` method returns the current high-resolution real time in a
`[seconds, nanoseconds]` tuple Array. `time` is an optional parameter that must
be the result of a previous `process.hrtime()` call (and therefore, a real time
in a `[seconds, nanoseconds]` tuple Array containing a previous time) to diff
with the current time. These times are relative to an arbitrary time in the
past, and not related to the time of day and therefore not subject to clock
drift. The primary use is for measuring performance between intervals.
* `time` {Array} The result of a previous call to `process.hrtime()`
* Returns: {Array}

The `process.hrtime()` method returns the current high-resolution real time
in a `[seconds, nanoseconds]` tuple Array, where `nanoseconds` is the
remaining part of the real time that can't be represented in second precision.

Passing in the result of a previous call to `process.hrtime()` is useful for
calculating an amount of time passed between calls:
`time` is an optional parameter that must be the result of a previous
`process.hrtime()` call to diff with the current time. If the parameter
passed in is not a tuple Array, a `TypeError` will be thrown. Passing in a
user-defined array instead of the result of a previous call to
`process.hrtime()` will lead to undefined behavior.

These times are relative to an arbitrary time in the
past, and not related to the time of day and therefore not subject to clock
drift. The primary use is for measuring performance between intervals:

```js
const NS_PER_SEC = 1e9;
var time = process.hrtime();
// [ 1800216, 25 ]

setTimeout(() => {
var diff = process.hrtime(time);
// [ 1, 552 ]

console.log(`Benchmark took ${diff[0] * 1e9 + diff[1]} nanoseconds`);
console.log(`Benchmark took ${diff[0] * NS_PER_SEC + diff[1]} nanoseconds`);
// benchmark took 1000000527 nanoseconds
}, 1000);
```

Constructing an array by some method other than calling `process.hrtime()` and
passing the result to process.hrtime() will result in undefined behavior.


## process.initgroups(user, extra_group)
<!-- YAML
Expand Down
18 changes: 10 additions & 8 deletions lib/internal/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,22 @@ function setup_cpuUsage() {
};
}


// The 3 entries filled in by the original process.hrtime contains
// the upper/lower 32 bits of the second part of the value,
// and the renamining nanoseconds of the value.
function setup_hrtime() {
const _hrtime = process.hrtime;
const hrValues = new Uint32Array(3);

process.hrtime = function hrtime(ar) {
process.hrtime = function hrtime(time) {
_hrtime(hrValues);

if (typeof ar !== 'undefined') {
if (Array.isArray(ar)) {
const sec = (hrValues[0] * 0x100000000 + hrValues[1]) - ar[0];
const nsec = hrValues[2] - ar[1];
return [nsec < 0 ? sec - 1 : sec, nsec < 0 ? nsec + 1e9 : nsec];
if (time !== undefined) {
if (Array.isArray(time) && time.length === 2) {
const sec = (hrValues[0] * 0x100000000 + hrValues[1]) - time[0];
const nsec = hrValues[2] - time[1];
const needsBorrow = nsec < 0;
return [needsBorrow ? sec - 1 : sec, needsBorrow ? nsec + 1e9 : nsec];
}

throw new TypeError('process.hrtime() only accepts an Array tuple');
Expand All @@ -98,7 +101,6 @@ function setup_hrtime() {
};
}


function setupConfig(_source) {
// NativeModule._source
// used for `process.config`, but not a real module
Expand Down
12 changes: 6 additions & 6 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2274,18 +2274,18 @@ void Kill(const FunctionCallbackInfo<Value>& args) {

// Hrtime exposes libuv's uv_hrtime() high-resolution timer.
// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds,
// so this function instead returns an Array with 2 entries representing seconds
// and nanoseconds, to avoid any integer overflow possibility.
// Pass in an Array from a previous hrtime() call to instead get a time diff.
// so this function instead fills in an Uint32Array with 3 entries,
// to avoid any integer overflow possibility.
// The first two entries contain the second part of the value
// broken into the upper/lower 32 bits to be converted back in JS,
// because there is no Uint64Array in JS.
// The third entry contains the remaining nanosecond part of the value.
void Hrtime(const FunctionCallbackInfo<Value>& args) {
uint64_t t = uv_hrtime();

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;
Expand Down
15 changes: 11 additions & 4 deletions test/parallel/test-process-hrtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,21 @@ validateTuple(process.hrtime(tuple));
assert.throws(() => {
process.hrtime(1);
}, /^TypeError: process.hrtime\(\) only accepts an Array tuple$/);
assert.throws(() => {
process.hrtime([]);
}, /^TypeError: process.hrtime\(\) only accepts an Array tuple$/);
assert.throws(() => {
process.hrtime([1]);
}, /^TypeError: process.hrtime\(\) only accepts an Array tuple$/);
assert.throws(() => {
process.hrtime([1, 2, 3]);
}, /^TypeError: process.hrtime\(\) only accepts an Array tuple$/);

function validateTuple(tuple) {
assert(Array.isArray(tuple));
assert.strictEqual(tuple.length, 2);
tuple.forEach((v) => {
assert.strictEqual(typeof v, 'number');
assert.strictEqual(isFinite(v), true);
});
assert(Number.isInteger(tuple[0]));
assert(Number.isInteger(tuple[1]));
}

const diff = process.hrtime([0, 1e9 - 1]);
Expand Down

0 comments on commit a647d82

Please sign in to comment.