Skip to content

Commit

Permalink
feat: use node histogram
Browse files Browse the repository at this point in the history
Use Node standard library histogram.
Closes #442.
  • Loading branch information
dnlup committed Nov 2, 2023
1 parent 60d6f09 commit eb20316
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 44 deletions.
34 changes: 14 additions & 20 deletions lib/gc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const { PerformanceObserver, constants } = require('perf_hooks')
const { PerformanceObserver, constants, createHistogram } = require('perf_hooks')
const {
NODE_PERFORMANCE_GC_MAJOR,
NODE_PERFORMANCE_GC_MINOR,
Expand All @@ -14,7 +14,6 @@ const {
NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY,
NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE
} = constants
const hdr = require('hdr-histogram-js')
const {
kSample,
kReset,
Expand All @@ -23,17 +22,13 @@ const {
kStop
} = require('./symbols')

hdr.initWebAssemblySync()

const kHistogram = Symbol('kHistogram')
const kTotalDuration = Symbol('kTotalDuration')

class GCEntry {
constructor () {
this[kHistogram] = hdr.build({
useWebAssembly: true,
lowestDiscernibleValue: 1,
highestTrackableValue: 1e10
this[kHistogram] = createHistogram({
lowestDiscernibleValue: 1
})
this[kTotalDuration] = 0
}
Expand All @@ -43,38 +38,37 @@ class GCEntry {
}

get totalCount () {
return this[kHistogram].totalCount
return this[kHistogram].count
}

get mean () {
return this[kHistogram].mean
}

get max () {
return this[kHistogram].maxValue
return this[kHistogram].max
}

// It seems this is not well supprted in hdr-histogram
// See https://github.com/HdrHistogram/HdrHistogramJS/issues/11
get min () {
return this[kHistogram].minNonZeroValue
return this[kHistogram].min
}

get stdDeviation () {
return this[kHistogram].stdDeviation
}

get summary () {
return this[kHistogram].summary
return this[kHistogram].stddev
}

getPercentile (percentile) {
return this[kHistogram].getValueAtPercentile(percentile)
return this[kHistogram].percentile(percentile)
}

[kSample] (ns) {
this[kTotalDuration] += ns
this[kHistogram].recordValue(ns)
/**
* We have to truncate the value here because `record`
* only accepts integer values:
* https://github.com/nodejs/node/blob/cdad3d8fe5f468aec6549fd59db73a3bfe063e3c/lib/internal/histogram.js#L283-L284
*/
this[kHistogram].record(Math.trunc(ns))
}

[kReset] () {
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@
"typescript": "^5.2.2"
},
"dependencies": {
"@dnlup/hrtime-utils": "^1.1.0",
"hdr-histogram-js": "^3.0.0"
"@dnlup/hrtime-utils": "^1.1.0"
}
}
6 changes: 3 additions & 3 deletions test/doc.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ test('eventLoopUtilization', { skip: !doc.eventLoopUtilizationSupported }, t =>
})
})

test('gc', t => {
test('gc', { only: true }, t => {
t.plan(25)
const sampler = doc({
gcOptions: {
Expand All @@ -184,7 +184,7 @@ test('gc', t => {
})
})

test('gc aggregation with flags', { skip: !doc.gcFlagsSupported }, t => {
test('gc aggregation with flags', t => {
t.plan(145)
const sampler = doc({
gcOptions: {
Expand Down Expand Up @@ -223,7 +223,7 @@ test('gc aggregation with flags', { skip: !doc.gcFlagsSupported }, t => {
})
})

test('resourceUsage', { skip: !doc.resourceUsageSupported }, t => {
test('resourceUsage', t => {
const sampler = doc({ collect: { resourceUsage: true } })
preventTestExitingEarly(t, 2000)
sampler.once('sample', () => {
Expand Down
44 changes: 25 additions & 19 deletions test/gc.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const {
kReset,
kObserverCallback
} = require('../lib/symbols')
const { gcFlagsSupported } = require('../lib/util')

test('garbage collection metric without aggregation', t => {
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Expand Down Expand Up @@ -46,11 +45,10 @@ test('garbage collection metric without aggregation', t => {
t.ok(gc.pause instanceof GCEntry)
t.equal(gc.pause.totalCount, 4 * data.length)
t.equal(gc.pause.totalDuration, 4 * entryDurationSum * 1e6)
t.equal(gc.pause.max, 10 * 1e6)
t.equal(gc.pause.min, 1 * 1e6)
t.equal(gc.pause.max, 10002431)
t.equal(gc.pause.min, 999936)
t.ok(gc.pause.mean > approxMean)
t.ok(gc.pause.getPercentile(99) > 10 * 1e6)
t.ok(typeof gc.pause.summary === 'object')
t.ok(gc.pause.stdDeviation > 0)

// Check it resets correctly
Expand All @@ -59,8 +57,6 @@ test('garbage collection metric without aggregation', t => {
t.equal(gc.pause.totalDuration, 0)
t.equal(gc.pause.max, 0)
// Not testing min value because it looks like is not initialized to zero.
// A possible discussion to follow:
// * https://github.com/HdrHistogram/HdrHistogramJS/issues/11
// t.equal(gc.pause.min, 0)
t.equal(gc.pause.getPercentile(99), 0)
t.end()
Expand Down Expand Up @@ -101,11 +97,10 @@ test('garbage collection metric with aggregation', t => {
t.ok(gc.pause instanceof GCEntry)
t.equal(gc.pause.totalCount, 4 * data.length)
t.equal(gc.pause.totalDuration, 4 * entryDurationSum * 1e6)
t.equal(gc.pause.max, 10 * 1e6)
t.equal(gc.pause.min, 1 * 1e6)
t.equal(gc.pause.max, 10002431)
t.equal(gc.pause.min, 999936)
t.ok(gc.pause.mean > approxMean)
t.ok(gc.pause.getPercentile(99) > 10 * 1e6)
t.ok(typeof gc.pause.summary === 'object')
t.ok(gc.pause.stdDeviation > 0)

for (const entry of [
Expand Down Expand Up @@ -138,15 +133,15 @@ test('garbage collection metric with aggregation', t => {
'weakCb'
]) {
const errorMessage = `Failed check for entry ${entry}`
t.equal(gc[entry].mean, 0, errorMessage)
t.ok(isNaN(gc[entry].mean), errorMessage)
t.equal(gc[entry].totalCount, 0, errorMessage)
t.ok(gc[entry].flags === undefined)
}

t.end()
})

test('garbage collection metric with aggregation and flags', { skip: !gcFlagsSupported }, t => {
test('garbage collection metric with aggregation and flags', t => {
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// Creates a fake list that's the same shape as PerformanceObserver
Expand Down Expand Up @@ -183,11 +178,10 @@ test('garbage collection metric with aggregation and flags', { skip: !gcFlagsSup
t.ok(gc.pause instanceof GCEntry)
t.equal(gc.pause.totalCount, 4 * data.length)
t.equal(gc.pause.totalDuration, 4 * entryDurationSum * 1e6)
t.equal(gc.pause.max, 10 * 1e6)
t.equal(gc.pause.min, 1 * 1e6)
t.equal(gc.pause.max, 10002431)
t.equal(gc.pause.min, 999936)
t.ok(gc.pause.mean > approxMean)
t.ok(gc.pause.getPercentile(99) > 10 * 1e6)
t.ok(typeof gc.pause.summary === 'object')
t.ok(gc.pause.stdDeviation > 0)

for (const entry of [
Expand Down Expand Up @@ -234,7 +228,14 @@ test('garbage collection metric with aggregation and flags', { skip: !gcFlagsSup
'totalDuration'
]) {
const errorMessage = `Failed check for ${entry}.flags.${flag}.${value}`
t.ok(gc.major.flags[flag][value] === 0, errorMessage)
switch (value) {
case 'mean':
case 'stdDeviation':
t.ok(isNaN(gc.major.flags[flag][value]), errorMessage)
break
default:
t.ok(gc.major.flags[flag][value] === 0, errorMessage)
}
}
}
}
Expand All @@ -257,7 +258,7 @@ test('garbage collection metric with aggregation and flags', { skip: !gcFlagsSup
'weakCb'
]) {
const errorMessage = `Failed check for entry ${entry}`
t.equal(gc[entry].mean, 0, errorMessage)
t.ok(isNaN(gc[entry].mean), errorMessage)
t.equal(gc[entry].totalCount, 0, errorMessage)
t.ok(gc.major.flags.no instanceof GCEntry)
t.equal(gc.major.flags.no.getPercentile(99), 0, errorMessage)
Expand All @@ -275,15 +276,20 @@ test('garbage collection metric with aggregation and flags', { skip: !gcFlagsSup
'mean',
'max',
// Not testing min value because it looks like is not initialized to zero.
// A possible discussion to follow:
// * https://github.com/HdrHistogram/HdrHistogramJS/issues/11
// 'min',
'stdDeviation',
'totalCount',
'totalDuration'
]) {
const errorMessage = `Failed check for ${entry}.flags.${flag}.${value}`
t.ok(gc.major.flags[flag][value] === 0, errorMessage)
switch (value) {
case 'mean':
case 'stdDeviation':
t.ok(isNaN(gc.major.flags[flag][value]), errorMessage)
break
default:
t.ok(gc.major.flags[flag][value] === 0, errorMessage)
}
}
}
}
Expand Down

0 comments on commit eb20316

Please sign in to comment.