Skip to content

Commit

Permalink
Reduce memory allocations/improve perf of Gauge
Browse files Browse the repository at this point in the history
  • Loading branch information
zbjornson committed Aug 4, 2021
1 parent 2335e00 commit 66f50b6
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 83 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Expand Up @@ -12,9 +12,10 @@ project adheres to [Semantic Versioning](http://semver.org/).
### Changed

- Don't add event listener to `process` if cluster module is not used.
- fix: set labels for default memory metrics on linux
- fix: fix DEP0152 deprecation warning in Node.js v16+
- fix: set labels for default memory metrics on linux.
- fix: fix DEP0152 deprecation warning in Node.js v16+.
- fix: Set aggregation mode for newer event loop metrics. (Fixes [#418](https://github.com/siimon/prom-client/issues/418))
- Improve performance of/reduce memory allocations in Gauge.

### Added

Expand Down
42 changes: 42 additions & 0 deletions benchmarks/gauge.js
@@ -0,0 +1,42 @@
'use strict';

const { getLabelNames, labelCombinationFactory } = require('./utils/labels');

module.exports = setupGaugeSuite;

function setupGaugeSuite(suite) {
suite.add(
'inc',
labelCombinationFactory([], (client, { Gauge }, labels) =>
Gauge.inc(labels, 1),
),
{ teardown, setup: setup(0) },
);

suite.add(
'inc with labels',
labelCombinationFactory([8, 8], (client, { Gauge }, labels) =>
Gauge.inc(labels, 1),
),
{ teardown, setup: setup(2) },
);
}

function setup(labelCount) {
return client => {
const registry = new client.Registry();

const Gauge = new client.Gauge({
name: 'Gauge',
help: 'Gauge',
labelNames: getLabelNames(labelCount),
registers: [registry],
});

return { registry, Gauge };
};
}

function teardown(client, { registry }) {
registry.clear();
}
1 change: 1 addition & 0 deletions benchmarks/index.js
Expand Up @@ -9,6 +9,7 @@ const benchmarks = createRegressionBenchmark(currentClient, [

benchmarks.suite('registry', require('./registry'));
benchmarks.suite('histogram', require('./histogram'));
benchmarks.suite('gauge', require('./gauge'));
benchmarks.suite('summary', require('./summary'));
benchmarks.run().catch(err => {
console.error(err.stack);
Expand Down
122 changes: 41 additions & 81 deletions lib/gauge.js
Expand Up @@ -24,18 +24,20 @@ class Gauge extends Metric {
* @returns {void}
*/
set(labels, value) {
if (!isObject(labels)) {
return set.call(this, null)(labels, value);
}
return set.call(this, labels)(value);
value = getValueArg(labels, value);
labels = getLabelArg(labels);
set(this, labels, value);
}

/**
* Reset gauge
* @returns {void}
*/
reset() {
return reset.call(this);
this.hashMap = {};
if (this.labelNames.length === 0) {
setValue(this.hashMap, 0, {});
}
}

/**
Expand All @@ -45,7 +47,10 @@ class Gauge extends Metric {
* @returns {void}
*/
inc(labels, value) {
inc.call(this, labels)(value);
value = getValueArg(labels, value);
labels = getLabelArg(labels);
if (value === undefined) value = 1;
set(this, labels, this._getValue(labels) + value);
}

/**
Expand All @@ -55,7 +60,10 @@ class Gauge extends Metric {
* @returns {void}
*/
dec(labels, value) {
dec.call(this, labels)(value);
value = getValueArg(labels, value);
labels = getLabelArg(labels);
if (value === undefined) value = 1;
set(this, labels, this._getValue(labels) - value);
}

/**
Expand All @@ -64,7 +72,12 @@ class Gauge extends Metric {
* @returns {void}
*/
setToCurrentTime(labels) {
return setToCurrentTime.call(this, labels)();
const now = Date.now() / 1000;
if (labels === undefined) {
this.set(now);
} else {
this.set(labels, now);
}
}

/**
Expand All @@ -78,7 +91,11 @@ class Gauge extends Metric {
* });
*/
startTimer(labels) {
return startTimer.call(this, labels)();
const start = process.hrtime();
return endLabels => {
const delta = process.hrtime(start);
this.set(Object.assign({}, labels, endLabels), delta[0] + delta[1] / 1e9);
};
}

async get() {
Expand All @@ -104,11 +121,11 @@ class Gauge extends Metric {
const labels = getLabels(this.labelNames, arguments);
validateLabel(this.labelNames, labels);
return {
inc: inc.call(this, labels),
dec: dec.call(this, labels),
set: set.call(this, labels),
setToCurrentTime: setToCurrentTime.call(this, labels),
startTimer: startTimer.call(this, labels),
inc: this.inc.bind(this, labels),
dec: this.dec.bind(this, labels),
set: this.set.bind(this, labels),
setToCurrentTime: this.setToCurrentTime.bind(this, labels),
startTimer: this.startTimer.bind(this, labels),
};
}

Expand All @@ -119,78 +136,21 @@ class Gauge extends Metric {
}
}

function setToCurrentTime(labels) {
return () => {
const now = Date.now() / 1000;
if (labels === undefined) {
this.set(now);
} else {
this.set(labels, now);
}
};
}

function startTimer(startLabels) {
return () => {
const start = process.hrtime();
return endLabels => {
const delta = process.hrtime(start);
this.set(
Object.assign({}, startLabels, endLabels),
delta[0] + delta[1] / 1e9,
);
};
};
}

function dec(labels) {
return value => {
const data = convertLabelsAndValues(labels, value);
if (data.value === undefined) data.value = 1;
this.set(data.labels, this._getValue(data.labels) - data.value);
};
}

function inc(labels) {
return value => {
const data = convertLabelsAndValues(labels, value);
if (data.value === undefined) data.value = 1;
this.set(data.labels, this._getValue(data.labels) + data.value);
};
}

function set(labels) {
return value => {
if (typeof value !== 'number') {
throw new TypeError(`Value is not a valid number: ${util.format(value)}`);
}

labels = labels || {};
function set(gauge, labels, value) {
if (typeof value !== 'number') {
throw new TypeError(`Value is not a valid number: ${util.format(value)}`);
}

validateLabel(this.labelNames, labels);
this.hashMap = setValue(this.hashMap, value, labels);
};
validateLabel(gauge.labelNames, labels);
setValue(gauge.hashMap, value, labels);
}

function reset() {
this.hashMap = {};

if (this.labelNames.length === 0) {
this.hashMap = setValue({}, 0, {});
}
function getLabelArg(labels) {
return isObject(labels) ? labels : {};
}

function convertLabelsAndValues(labels, value) {
if (!isObject(labels)) {
return {
value: labels,
labels: {},
};
}
return {
labels,
value,
};
function getValueArg(labels, value) {
return isObject(labels) ? value : labels;
}

module.exports = Gauge;

0 comments on commit 66f50b6

Please sign in to comment.