diff --git a/benchmark/perf_hooks/histogram-snapshot.js b/benchmark/perf_hooks/histogram-snapshot.js new file mode 100644 index 000000000000..79b13fecb0f6 --- /dev/null +++ b/benchmark/perf_hooks/histogram-snapshot.js @@ -0,0 +1,24 @@ +'use strict'; + +const assert = require('assert'); +const common = require('../common.js'); +const { createHistogram } = require('perf_hooks'); + +const bench = common.createBenchmark(main, { + n: [1e3], + highest: [1e6, Number.MAX_SAFE_INTEGER], + figures: [2, 3], +}); + +let snapshot; + +function main({ n, highest, figures }) { + const histogram = createHistogram({ highest, figures }); + for (let i = 1; i <= 1e4; i++) histogram.record(i); + + bench.start(); + for (let i = 0; i < n; i++) snapshot = histogram.snapshot(); + bench.end(n); + + assert.strictEqual(snapshot.count, 1e4); +} diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 69a4d6613b9b..307a42eda267 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -2152,6 +2152,52 @@ added: Returns the number of recorded values that fall within the equivalent value range of the given value. +### `histogram.diff(other)` + + + +* `other` {Histogram} An earlier snapshot of this histogram. +* Returns: {Histogram} + +Returns a new {Histogram} containing the values recorded in this histogram after +`other` was taken. Neither histogram is changed. To get the values recorded +during each interval without calling `reset()`, compute each difference from a +snapshot and keep that snapshot as the baseline for the next interval: + +```js +const { monitorEventLoopDelay } = require('node:perf_hooks'); + +const histogram = monitorEventLoopDelay(); +histogram.enable(); +let previous = histogram.snapshot(); + +setInterval(() => { + const current = histogram.snapshot(); + // After a reset, use everything recorded since the reset. + const delta = current.resetCount === previous.resetCount ? + current.diff(previous) : current; + console.log(delta.percentile(99)); + previous = current; +}, 10_000); +``` + +The `count`, `exceeds`, and bucket counts of the returned histogram are the +differences between the two histograms. Its `min` and `max` are computed from +the buckets of the difference, it has no EWMA state, and its `resetCount` is +`0`. + +This method throws: + +* `ERR_INVALID_ARG_VALUE` if `other` has a different `lowest`, `highest`, or + `figures` configuration. +* `ERR_INVALID_STATE` if values have been removed from this histogram since + `other` was taken, which is the case when the `resetCount` of the two + histograms differs. +* `ERR_INVALID_ARG_VALUE` if `other` contains values that are not in this + histogram, for example because the histograms were passed in the wrong order. + ### `histogram.exceeds` -Resets the collected histogram data. +Resets the collected histogram data and increments `histogram.resetCount`. + +### `histogram.resetCount` + + + +* Type: {number} + +The number of times values have been removed from this histogram by `reset()` +or, for a {RecordableHistogram}, `subtract()`. A snapshot has the `resetCount` +of its source at the time it was taken, so comparing the `resetCount` of two +snapshots shows whether the source was reset between them. See +[`histogram.diff()`][]. ### `histogram.skewness` @@ -2621,6 +2681,38 @@ distribution. A positive value indicates a right-skewed distribution (longer right tail, common for latency data); a negative value indicates a left-skewed distribution. +### `histogram.snapshot()` + + + +* Returns: {Histogram} + +Returns a new, independent {Histogram} containing a copy of this histogram's +current state: its configuration, recorded values, `exceeds` count, and EWMA +state. Values recorded into this histogram after this method returns, and later +calls to `reset()`, do not change the returned histogram. This provides a stable +view of a histogram that is still recording, such as an enabled {ELDHistogram}. + +Values cannot be recorded into the returned histogram. Taking a snapshot copies +every bucket, so both its time and memory cost depend on the histogram's +`lowest`, `highest`, and `figures` configuration rather than on the number of +recorded values. + +```js +const { monitorEventLoopDelay } = require('node:perf_hooks'); + +const histogram = monitorEventLoopDelay(); +histogram.enable(); + +setTimeout(() => { + const snapshot = histogram.snapshot(); + console.log(snapshot.percentile(99)); + histogram.disable(); +}, 1000); +``` + ### `histogram.stddev`