Skip to content

Commit 66f50b6

Browse files
committed
Reduce memory allocations/improve perf of Gauge
1 parent 2335e00 commit 66f50b6

File tree

4 files changed

+87
-83
lines changed

4 files changed

+87
-83
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ project adheres to [Semantic Versioning](http://semver.org/).
1212
### Changed
1313

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

1920
### Added
2021

benchmarks/gauge.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
3+
const { getLabelNames, labelCombinationFactory } = require('./utils/labels');
4+
5+
module.exports = setupGaugeSuite;
6+
7+
function setupGaugeSuite(suite) {
8+
suite.add(
9+
'inc',
10+
labelCombinationFactory([], (client, { Gauge }, labels) =>
11+
Gauge.inc(labels, 1),
12+
),
13+
{ teardown, setup: setup(0) },
14+
);
15+
16+
suite.add(
17+
'inc with labels',
18+
labelCombinationFactory([8, 8], (client, { Gauge }, labels) =>
19+
Gauge.inc(labels, 1),
20+
),
21+
{ teardown, setup: setup(2) },
22+
);
23+
}
24+
25+
function setup(labelCount) {
26+
return client => {
27+
const registry = new client.Registry();
28+
29+
const Gauge = new client.Gauge({
30+
name: 'Gauge',
31+
help: 'Gauge',
32+
labelNames: getLabelNames(labelCount),
33+
registers: [registry],
34+
});
35+
36+
return { registry, Gauge };
37+
};
38+
}
39+
40+
function teardown(client, { registry }) {
41+
registry.clear();
42+
}

benchmarks/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const benchmarks = createRegressionBenchmark(currentClient, [
99

1010
benchmarks.suite('registry', require('./registry'));
1111
benchmarks.suite('histogram', require('./histogram'));
12+
benchmarks.suite('gauge', require('./gauge'));
1213
benchmarks.suite('summary', require('./summary'));
1314
benchmarks.run().catch(err => {
1415
console.error(err.stack);

lib/gauge.js

Lines changed: 41 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,20 @@ class Gauge extends Metric {
2424
* @returns {void}
2525
*/
2626
set(labels, value) {
27-
if (!isObject(labels)) {
28-
return set.call(this, null)(labels, value);
29-
}
30-
return set.call(this, labels)(value);
27+
value = getValueArg(labels, value);
28+
labels = getLabelArg(labels);
29+
set(this, labels, value);
3130
}
3231

3332
/**
3433
* Reset gauge
3534
* @returns {void}
3635
*/
3736
reset() {
38-
return reset.call(this);
37+
this.hashMap = {};
38+
if (this.labelNames.length === 0) {
39+
setValue(this.hashMap, 0, {});
40+
}
3941
}
4042

4143
/**
@@ -45,7 +47,10 @@ class Gauge extends Metric {
4547
* @returns {void}
4648
*/
4749
inc(labels, value) {
48-
inc.call(this, labels)(value);
50+
value = getValueArg(labels, value);
51+
labels = getLabelArg(labels);
52+
if (value === undefined) value = 1;
53+
set(this, labels, this._getValue(labels) + value);
4954
}
5055

5156
/**
@@ -55,7 +60,10 @@ class Gauge extends Metric {
5560
* @returns {void}
5661
*/
5762
dec(labels, value) {
58-
dec.call(this, labels)(value);
63+
value = getValueArg(labels, value);
64+
labels = getLabelArg(labels);
65+
if (value === undefined) value = 1;
66+
set(this, labels, this._getValue(labels) - value);
5967
}
6068

6169
/**
@@ -64,7 +72,12 @@ class Gauge extends Metric {
6472
* @returns {void}
6573
*/
6674
setToCurrentTime(labels) {
67-
return setToCurrentTime.call(this, labels)();
75+
const now = Date.now() / 1000;
76+
if (labels === undefined) {
77+
this.set(now);
78+
} else {
79+
this.set(labels, now);
80+
}
6881
}
6982

7083
/**
@@ -78,7 +91,11 @@ class Gauge extends Metric {
7891
* });
7992
*/
8093
startTimer(labels) {
81-
return startTimer.call(this, labels)();
94+
const start = process.hrtime();
95+
return endLabels => {
96+
const delta = process.hrtime(start);
97+
this.set(Object.assign({}, labels, endLabels), delta[0] + delta[1] / 1e9);
98+
};
8299
}
83100

84101
async get() {
@@ -104,11 +121,11 @@ class Gauge extends Metric {
104121
const labels = getLabels(this.labelNames, arguments);
105122
validateLabel(this.labelNames, labels);
106123
return {
107-
inc: inc.call(this, labels),
108-
dec: dec.call(this, labels),
109-
set: set.call(this, labels),
110-
setToCurrentTime: setToCurrentTime.call(this, labels),
111-
startTimer: startTimer.call(this, labels),
124+
inc: this.inc.bind(this, labels),
125+
dec: this.dec.bind(this, labels),
126+
set: this.set.bind(this, labels),
127+
setToCurrentTime: this.setToCurrentTime.bind(this, labels),
128+
startTimer: this.startTimer.bind(this, labels),
112129
};
113130
}
114131

@@ -119,78 +136,21 @@ class Gauge extends Metric {
119136
}
120137
}
121138

122-
function setToCurrentTime(labels) {
123-
return () => {
124-
const now = Date.now() / 1000;
125-
if (labels === undefined) {
126-
this.set(now);
127-
} else {
128-
this.set(labels, now);
129-
}
130-
};
131-
}
132-
133-
function startTimer(startLabels) {
134-
return () => {
135-
const start = process.hrtime();
136-
return endLabels => {
137-
const delta = process.hrtime(start);
138-
this.set(
139-
Object.assign({}, startLabels, endLabels),
140-
delta[0] + delta[1] / 1e9,
141-
);
142-
};
143-
};
144-
}
145-
146-
function dec(labels) {
147-
return value => {
148-
const data = convertLabelsAndValues(labels, value);
149-
if (data.value === undefined) data.value = 1;
150-
this.set(data.labels, this._getValue(data.labels) - data.value);
151-
};
152-
}
153-
154-
function inc(labels) {
155-
return value => {
156-
const data = convertLabelsAndValues(labels, value);
157-
if (data.value === undefined) data.value = 1;
158-
this.set(data.labels, this._getValue(data.labels) + data.value);
159-
};
160-
}
161-
162-
function set(labels) {
163-
return value => {
164-
if (typeof value !== 'number') {
165-
throw new TypeError(`Value is not a valid number: ${util.format(value)}`);
166-
}
167-
168-
labels = labels || {};
139+
function set(gauge, labels, value) {
140+
if (typeof value !== 'number') {
141+
throw new TypeError(`Value is not a valid number: ${util.format(value)}`);
142+
}
169143

170-
validateLabel(this.labelNames, labels);
171-
this.hashMap = setValue(this.hashMap, value, labels);
172-
};
144+
validateLabel(gauge.labelNames, labels);
145+
setValue(gauge.hashMap, value, labels);
173146
}
174147

175-
function reset() {
176-
this.hashMap = {};
177-
178-
if (this.labelNames.length === 0) {
179-
this.hashMap = setValue({}, 0, {});
180-
}
148+
function getLabelArg(labels) {
149+
return isObject(labels) ? labels : {};
181150
}
182151

183-
function convertLabelsAndValues(labels, value) {
184-
if (!isObject(labels)) {
185-
return {
186-
value: labels,
187-
labels: {},
188-
};
189-
}
190-
return {
191-
labels,
192-
value,
193-
};
152+
function getValueArg(labels, value) {
153+
return isObject(labels) ? value : labels;
194154
}
195155

196156
module.exports = Gauge;

0 commit comments

Comments
 (0)