From f4083bd1f74ca5788ffb8c026e9ba65b34d903a6 Mon Sep 17 00:00:00 2001 From: Joachim Viide Date: Sun, 7 Jul 2024 01:23:56 +0000 Subject: [PATCH 1/3] fix(range): optimize space character normalization Produce fewer intermediate objects when a range's space characters are reduced to single spaces. This appears to have about 5% performance benefit for bench-subset, and smaller but detectable impact on bench-satisfies. --- classes/range.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/classes/range.js b/classes/range.js index 117b45a2..56701855 100644 --- a/classes/range.js +++ b/classes/range.js @@ -1,3 +1,5 @@ +const SPACE_CHARACTERS = /\s+/g + // hoisted class for cyclic dependency class Range { constructor (range, options) { @@ -29,10 +31,7 @@ class Range { // First reduce all whitespace as much as possible so we do not have to rely // on potentially slow regexes like \s*. This is then stored and used for // future error messages as well. - this.raw = range - .trim() - .split(/\s+/) - .join(' ') + this.raw = range.trim().replace(SPACE_CHARACTERS, ' ') // First, split on || this.set = this.raw From a9f61b8639b162966f355738efdb6a24315d0227 Mon Sep 17 00:00:00 2001 From: Joachim Viide Date: Tue, 9 Jul 2024 22:56:13 +0000 Subject: [PATCH 2/3] fix(range): optimize range formatting This speeds bench-subset and bench-satisfies by up to 20%. --- classes/range.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/classes/range.js b/classes/range.js index 56701855..3f61ecad 100644 --- a/classes/range.js +++ b/classes/range.js @@ -69,10 +69,19 @@ class Range { } format () { - this.range = this.set - .map((comps) => comps.join(' ').trim()) - .join('||') - .trim() + this.range = '' + for (let i = 0; i < this.set.length; i++) { + if (i > 0) { + this.range += '||' + } + const comps = this.set[i] + for (let k = 0; k < comps.length; k++) { + if (k > 0) { + this.range += ' ' + } + this.range += comps[k].toString().trim() + } + } return this.range } From 3f5ec0be28d58a0e849b233d3060e9d50d97c1e4 Mon Sep 17 00:00:00 2001 From: Joachim Viide Date: Sun, 7 Jul 2024 01:32:39 +0000 Subject: [PATCH 3/3] fix(range): compute formatted value lazily This speeds bench-subset and bench-satisfies by up to 9%. The external interface of the Range class is kept as-is except that the .range property is not writable anymore. format test --- classes/range.js | 32 +++++++++++++++++++------------- test/classes/range.js | 9 +++++++++ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/classes/range.js b/classes/range.js index 3f61ecad..ceee2314 100644 --- a/classes/range.js +++ b/classes/range.js @@ -20,7 +20,7 @@ class Range { // just put it in the set and return this.raw = range.value this.set = [[range]] - this.format() + this.formatted = undefined return this } @@ -65,23 +65,29 @@ class Range { } } - this.format() + this.formatted = undefined } - format () { - this.range = '' - for (let i = 0; i < this.set.length; i++) { - if (i > 0) { - this.range += '||' - } - const comps = this.set[i] - for (let k = 0; k < comps.length; k++) { - if (k > 0) { - this.range += ' ' + get range () { + if (this.formatted === undefined) { + this.formatted = '' + for (let i = 0; i < this.set.length; i++) { + if (i > 0) { + this.formatted += '||' + } + const comps = this.set[i] + for (let k = 0; k < comps.length; k++) { + if (k > 0) { + this.formatted += ' ' + } + this.formatted += comps[k].toString().trim() } - this.range += comps[k].toString().trim() } } + return this.formatted + } + + format () { return this.range } diff --git a/test/classes/range.js b/test/classes/range.js index 9547e23a..c1e6eb1b 100644 --- a/test/classes/range.js +++ b/test/classes/range.js @@ -82,6 +82,15 @@ test('tostrings', (t) => { t.end() }) +test('formatted value is calculated lazily and cached', (t) => { + const r = new Range('>= v1.2.3') + t.equal(r.formatted, undefined) + t.equal(r.format(), '>=1.2.3') + t.equal(r.formatted, '>=1.2.3') + t.equal(r.format(), '>=1.2.3') + t.end() +}) + test('ranges intersect', (t) => { rangeIntersection.forEach(([r0, r1, expect]) => { t.test(`${r0} <~> ${r1}`, t => {