monitor-event-loop-delay
Version:
Polyfill for perf_hooks.monitorEventLoopDelay(...)
130 lines (100 loc) • 2.92 kB
JavaScript
'use strict'
function toNano (t) {
return (t[0] * 1e9) + t[1]
}
function sumReducer (last, next) {
return last + next
}
function sortNumber (a, b) {
a = Number.isNaN(a) ? Number.NEGATIVE_INFINITY : a
b = Number.isNaN(b) ? Number.NEGATIVE_INFINITY : b
if (a > b) return 1
if (a < b) return -1
return 0
}
function descriptor (target, property, data) {
Object.defineProperty(target, property, data)
}
function define (target, property, value) {
descriptor(target, property, { value })
}
function mean (list) {
return list.reduce(sumReducer, 0) / list.length
}
class EventLoopDelayHistogram {
constructor ({ resolution = 10 } = {}) {
define(this, 'resolution', resolution)
descriptor(this, 'timer', { writable: true })
descriptor(this, 'samples', { writable: true, value: [] })
}
get stddev () {
const avg = mean(this.samples)
const squareDiffs = this.samples.map(value => {
const diff = value - avg
const sqrDiff = diff * diff
return sqrDiff
})
return Math.sqrt(mean(squareDiffs))
}
get mean () {
return mean(this.samples)
}
get min () {
// This giant number is only present to emulate edge case behaviour.
// When not yet enabled, this will be the value of min.
return Math.min(9223372036854776000, ...this.samples)
}
get max () {
return Math.max(0, ...this.samples)
}
get percentiles () {
const map = new Map()
let last = 0
if (this.samples.length) {
map.set(0, this.percentile(0))
for (let percent = 50; percent < 100; percent += (100 - percent) / 2) {
const next = this.percentile(percent)
if (last === next) break
map.set(percent, next)
last = next
}
}
map.set(100, this.percentile(100))
return map
}
percentile (percent) {
percent = Number(percent)
if (isNaN(percent) || percent < 0 || percent > 100) {
throw new TypeError('Percent must be a floating point number between 0 and 100')
}
const list = this.samples.sort(sortNumber)
if (percent === 0) return list[0]
return list[Math.ceil(list.length * (percent / 100)) - 1] || 0
}
enable () {
if (this.timer) return false
let last = process.hrtime()
this.timer = setInterval(() => {
const next = process.hrtime(last)
this.samples.push(Math.max(0, toNano(next)))
last = process.hrtime()
}, this.resolution)
this.timer.unref()
return true
}
disable () {
if (!this.timer) return false
clearInterval(this.timer)
this.timer = null
return true
}
reset () {
this.samples = []
}
}
const proto = EventLoopDelayHistogram.prototype
descriptor(proto, 'stddev', { enumerable: true })
descriptor(proto, 'mean', { enumerable: true })
descriptor(proto, 'min', { enumerable: true })
descriptor(proto, 'max', { enumerable: true })
module.exports = EventLoopDelayHistogram