Skip to content

Commit b803bca

Browse files
committed
perf_hooks: add histogram option to timerify
Allows setting a `Histogram` object option on timerify to record function execution times over time. Signed-off-by: James M Snell <[email protected]> PR-URL: #37475 Reviewed-By: Matteo Collina <[email protected]>
1 parent 9c1274a commit b803bca

File tree

4 files changed

+82
-5
lines changed

4 files changed

+82
-5
lines changed

‎doc/api/perf_hooks.md‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,17 +210,24 @@ added: v8.5.0
210210
The [`timeOrigin`][] specifies the high resolution millisecond timestamp at
211211
which the current `node` process began, measured in Unix time.
212212

213-
### `performance.timerify(fn)`
213+
### `performance.timerify(fn[, options])`
214214
<!-- YAML
215215
added: v8.5.0
216216
changes:
217+
- version: REPLACEME
218+
pr-url: https://github.com/nodejs/node/pull/37475
219+
description: Added the histogram option.
217220
- version: REPLACEME
218221
pr-url: https://github.com/nodejs/node/pull/37136
219222
description: Re-implemented to use pure-JavaScript and the ability
220223
to time async functions.
221224
-->
222225

223226
*`fn`{Function}
227+
*`options`{Object}
228+
*`histogram`{RecordableHistogram} A histogram object created using
229+
`perf_hooks.createHistogram()` that will record runtime durations in
230+
nanoseconds.
224231

225232
_This property is an extension by Node.js. It is not available in Web browsers._
226233

‎lib/internal/histogram.js‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ const{
4242
JSTransferable,
4343
}=require('internal/worker/js_transferable');
4444

45+
functionisHistogram(object){
46+
returnobject?.[kHandle]!==undefined;
47+
}
48+
4549
classHistogramextendsJSTransferable{
4650
constructor(internal){
4751
super();
@@ -193,6 +197,7 @@ module.exports ={
193197
RecordableHistogram,
194198
InternalHistogram,
195199
InternalRecordableHistogram,
200+
isHistogram,
196201
kDestroy,
197202
kHandle,
198203
createHistogram,

‎lib/internal/perf/timerify.js‎

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const{
44
FunctionPrototypeBind,
55
ObjectDefineProperties,
6+
MathCeil,
67
ReflectApply,
78
ReflectConstruct,
89
Symbol,
@@ -13,6 +14,14 @@ const{
1314
now,
1415
}=require('internal/perf/perf');
1516

17+
const{
18+
validateObject
19+
}=require('internal/validators');
20+
21+
const{
22+
isHistogram
23+
}=require('internal/histogram');
24+
1625
const{
1726
isConstructor,
1827
}=internalBinding('util');
@@ -29,8 +38,10 @@ const{
2938

3039
constkTimerified=Symbol('kTimerified');
3140

32-
functionprocessComplete(name,start,args){
41+
functionprocessComplete(name,start,args,histogram){
3342
constduration=now()-start;
43+
if(histogram!==undefined)
44+
histogram.record(MathCeil(duration*1e6));
3445
constentry=
3546
newInternalPerformanceEntry(
3647
name,
@@ -45,10 +56,23 @@ function processComplete(name, start, args){
4556
enqueue(entry);
4657
}
4758

48-
functiontimerify(fn){
59+
functiontimerify(fn,options={}){
4960
if(typeoffn!=='function')
5061
thrownewERR_INVALID_ARG_TYPE('fn','function',fn);
5162

63+
validateObject(options,'options');
64+
const{
65+
histogram
66+
}=options;
67+
68+
if(histogram!==undefined&&
69+
(!isHistogram(histogram)||typeofhistogram.record!=='function')){
70+
thrownewERR_INVALID_ARG_TYPE(
71+
'options.histogram',
72+
'RecordableHistogram',
73+
histogram);
74+
}
75+
5276
if(fn[kTimerified])returnfn[kTimerified];
5377

5478
constconstructor=isConstructor(fn);
@@ -65,9 +89,10 @@ function timerify(fn){
6589
result,
6690
fn.name,
6791
start,
68-
args));
92+
args,
93+
histogram));
6994
}
70-
processComplete(fn.name,start,args);
95+
processComplete(fn.name,start,args,histogram);
7196
returnresult;
7297
}
7398

‎test/parallel/test-performance-function.js‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@ const common = require('../common');
44
constassert=require('assert');
55

66
const{
7+
createHistogram,
78
performance,
89
PerformanceObserver
910
}=require('perf_hooks');
1011

12+
const{
13+
setTimeout: sleep
14+
}=require('timers/promises');
15+
1116
{
1217
// Intentional non-op. Do not wrap in common.mustCall();
1318
constn=performance.timerify(functionnoop(){});
@@ -81,3 +86,38 @@ const{
8186
assert.strictEqual(n.length,m.length);
8287
assert.strictEqual(n.name,'timerified m');
8388
}
89+
90+
(async()=>{
91+
consthistogram=createHistogram();
92+
constm=(a,b=1)=>{};
93+
constn=performance.timerify(m,{ histogram });
94+
assert.strictEqual(histogram.max,0);
95+
for(leti=0;i<10;i++){
96+
n();
97+
awaitsleep(10);
98+
}
99+
assert.notStrictEqual(histogram.max,0);
100+
[1,'',{},[],false].forEach((histogram)=>{
101+
assert.throws(()=>performance.timerify(m,{ histogram }),{
102+
code: 'ERR_INVALID_ARG_TYPE'
103+
});
104+
});
105+
})().then(common.mustCall());
106+
107+
(async()=>{
108+
consthistogram=createHistogram();
109+
constm=async(a,b=1)=>{
110+
awaitsleep(10);
111+
};
112+
constn=performance.timerify(m,{ histogram });
113+
assert.strictEqual(histogram.max,0);
114+
for(leti=0;i<10;i++){
115+
awaitn();
116+
}
117+
assert.notStrictEqual(histogram.max,0);
118+
[1,'',{},[],false].forEach((histogram)=>{
119+
assert.throws(()=>performance.timerify(m,{ histogram }),{
120+
code: 'ERR_INVALID_ARG_TYPE'
121+
});
122+
});
123+
})().then(common.mustCall());

0 commit comments

Comments
(0)