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

process: improve process.hrtime #10764

Closed
wants to merge 1 commit into from
Closed
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
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]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding another test that has > 2 elements?

}, /^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