diff --git a/.eslintrc b/.eslintrc index 067ab963..5055dbf3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,7 +10,7 @@ "es6": true }, "parserOptions": { - "ecmaVersion": 2019 + "ecmaVersion": 2020 }, "rules": { "no-underscore-dangle": "off", diff --git a/CHANGELOG.md b/CHANGELOG.md index 1617de59..712a299d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,12 @@ project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- Improve the memory usage of histograms by delaying allocation of bucket counters in `bucketValues` until their value is > 0 + (Use a prototype of the initial counters) +- Improve the memory usage of histograms by allocating empty `enableExemplars` instead of pre-filling with `null` +- Minor performance improvements: + - Uvoid unneeded hash table updates + - Avoid hash table lookup for examplars - Improve the memory usage of histograms when the `enableExemplars` option is disabled ### Added diff --git a/lib/histogram.js b/lib/histogram.js index 22b65340..e5b2c611 100644 --- a/lib/histogram.js +++ b/lib/histogram.js @@ -24,7 +24,7 @@ class Histogram extends Metric { this.type = 'histogram'; this.defaultLabels = {}; this.defaultExemplarLabelSet = {}; - this.enableExemplars = false; + this.enableExemplars = config.enableExemplars; for (const label of this.labelNames) { if (label === 'le') { @@ -38,19 +38,10 @@ class Histogram extends Metric { return acc; }, {}); - if (config.enableExemplars) { - this.enableExemplars = true; - this.bucketExemplars = this.upperBounds.reduce((acc, upperBound) => { - acc[upperBound] = null; - return acc; - }, {}); - Object.freeze(this.bucketExemplars); - this.observe = this.observeWithExemplar; - } else { - this.observe = this.observeWithoutExemplar; - } + this.observe = this.enableExemplars + ? this.observeWithExemplar + : this.observeWithoutExemplar; - Object.freeze(this.bucketValues); Object.freeze(this.upperBounds); if (this.labelNames.length === 0) { @@ -58,7 +49,7 @@ class Histogram extends Metric { [hashObject({})]: createBaseValues( {}, this.bucketValues, - this.bucketExemplars, + this.enableExemplars, ), }; } @@ -79,14 +70,15 @@ class Histogram extends Metric { value, exemplarLabels = this.defaultExemplarLabelSet, } = {}) { - observe.call(this, labels === 0 ? 0 : labels || {})(value); - this.updateExemplar(labels, value, exemplarLabels); + const valueFromMap = observe.call( + this, + labels === 0 ? 0 : labels || {}, + )(value); + this.updateExemplar(valueFromMap, value, exemplarLabels); } - updateExemplar(labels, value, exemplarLabels) { - const hash = hashObject(labels, this.sortedLabelNames); + updateExemplar({ bucketExemplars }, value, exemplarLabels) { const bound = findBound(this.upperBounds, value); - const { bucketExemplars } = this.hashMap[hash]; let exemplar = bucketExemplars[bound]; if (!isObject(exemplar)) { exemplar = new Exemplar(); @@ -137,7 +129,7 @@ class Histogram extends Metric { this.hashMap[hash] = createBaseValues( labels, this.bucketValues, - this.bucketExemplars, + this.enableExemplars, ); } @@ -244,8 +236,9 @@ function observe(labels) { valueFromMap = createBaseValues( labelValuePair.labels, this.bucketValues, - this.bucketExemplars, + this.enableExemplars, ); + this.hashMap[hash] = valueFromMap; } const b = findBound(this.upperBounds, labelValuePair.value); @@ -253,23 +246,23 @@ function observe(labels) { valueFromMap.sum += labelValuePair.value; valueFromMap.count += 1; - if (Object.prototype.hasOwnProperty.call(valueFromMap.bucketValues, b)) { + if (typeof valueFromMap.bucketValues[b] === 'number') { valueFromMap.bucketValues[b] += 1; } - this.hashMap[hash] = valueFromMap; + return valueFromMap; }; } -function createBaseValues(labels, bucketValues, bucketExemplars) { +function createBaseValues(labels, bucketValues, enableExemplars) { const result = { labels, - bucketValues: { ...bucketValues }, + bucketValues: Object.create(bucketValues), sum: 0, count: 0, }; - if (bucketExemplars) { - result.bucketExemplars = { ...bucketExemplars }; + if (enableExemplars) { + result.bucketExemplars = {}; } return result; } @@ -290,16 +283,15 @@ function extractBucketValuesForExport(histogram) { const name = `${histogram.name}_bucket`; return bucketData => { let acc = 0; + const { labels, bucketValues, bucketExemplars } = bucketData; const buckets = histogram.upperBounds.map(upperBound => { - acc += bucketData.bucketValues[upperBound]; + acc += bucketValues[upperBound]; return setValuePair( { le: upperBound }, acc, name, - bucketData.bucketExemplars - ? bucketData.bucketExemplars[upperBound] - : null, - bucketData.labels, + bucketExemplars?.[upperBound] ?? null, + labels, ); }); return { buckets, data: bucketData };