From bcdd228f90b3e9e428b584814e7d52627616456a Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 7 Jan 2019 11:36:35 -0800 Subject: perf_hooks: implement histogram based api Add a sampling-based event loop delay monitor. ```js const { monitorEventLoopDelay } = require('perf_hooks'); const h = monitorEventLoopDelay(); h.enable(); h.disable(); console.log(h.percentiles); console.log(h.min); console.log(h.max); console.log(h.mean); console.log(h.stddev); console.log(h.percentile(50)); ``` PR-URL: https://github.com/nodejs/node/pull/25378 Reviewed-By: Matteo Collina Reviewed-By: Gireesh Punathil Reviewed-By: Stephen Belanger Reviewed-By: Richard Lau Reviewed-By: Anna Henningsen --- lib/perf_hooks.js | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) (limited to 'lib/perf_hooks.js') diff --git a/lib/perf_hooks.js b/lib/perf_hooks.js index 583ffb56ca..b1d634bf3c 100644 --- a/lib/perf_hooks.js +++ b/lib/perf_hooks.js @@ -1,6 +1,7 @@ 'use strict'; const { + ELDHistogram: _ELDHistogram, PerformanceEntry, mark: _mark, clearMark: _clearMark, @@ -35,6 +36,8 @@ const { AsyncResource } = require('async_hooks'); const L = require('internal/linkedlist'); const kInspect = require('internal/util').customInspectSymbol; +const kHandle = Symbol('handle'); +const kMap = Symbol('map'); const kCallback = Symbol('callback'); const kTypes = Symbol('types'); const kEntries = Symbol('entries'); @@ -545,9 +548,73 @@ function sortedInsert(list, entry) { list.splice(location, 0, entry); } +class ELDHistogram { + constructor(handle) { + this[kHandle] = handle; + this[kMap] = new Map(); + } + + reset() { this[kHandle].reset(); } + enable() { return this[kHandle].enable(); } + disable() { return this[kHandle].disable(); } + + get exceeds() { return this[kHandle].exceeds(); } + get min() { return this[kHandle].min(); } + get max() { return this[kHandle].max(); } + get mean() { return this[kHandle].mean(); } + get stddev() { return this[kHandle].stddev(); } + percentile(percentile) { + if (typeof percentile !== 'number') { + const errors = lazyErrors(); + throw new errors.ERR_INVALID_ARG_TYPE('percentile', 'number', percentile); + } + if (percentile <= 0 || percentile > 100) { + const errors = lazyErrors(); + throw new errors.ERR_INVALID_ARG_VALUE.RangeError('percentile', + percentile); + } + return this[kHandle].percentile(percentile); + } + get percentiles() { + this[kMap].clear(); + this[kHandle].percentiles(this[kMap]); + return this[kMap]; + } + + [kInspect]() { + return { + min: this.min, + max: this.max, + mean: this.mean, + stddev: this.stddev, + percentiles: this.percentiles, + exceeds: this.exceeds + }; + } +} + +function monitorEventLoopDelay(options = {}) { + if (typeof options !== 'object' || options === null) { + const errors = lazyErrors(); + throw new errors.ERR_INVALID_ARG_TYPE('options', 'Object', options); + } + const { resolution = 10 } = options; + if (typeof resolution !== 'number') { + const errors = lazyErrors(); + throw new errors.ERR_INVALID_ARG_TYPE('options.resolution', + 'number', resolution); + } + if (resolution <= 0 || !Number.isSafeInteger(resolution)) { + const errors = lazyErrors(); + throw new errors.ERR_INVALID_OPT_VALUE.RangeError('resolution', resolution); + } + return new ELDHistogram(new _ELDHistogram(resolution)); +} + module.exports = { performance, - PerformanceObserver + PerformanceObserver, + monitorEventLoopDelay }; Object.defineProperty(module.exports, 'constants', { -- cgit v1.2.3