From ca185a012e055171bffc04edf5965a8bfa6ae74f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Thu, 30 Mar 2023 21:33:43 -0300 Subject: [PATCH 1/3] perf: improving perf of parsing options --- internal/parse-options.js | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/internal/parse-options.js b/internal/parse-options.js index bbd9ec77..7cbcf16c 100644 --- a/internal/parse-options.js +++ b/internal/parse-options.js @@ -1,11 +1,17 @@ -// parse out just the options we care about so we always get a consistent -// obj with keys in a consistent order. -const opts = ['includePrerelease', 'loose', 'rtl'] -const parseOptions = options => - !options ? {} - : typeof options !== 'object' ? { loose: true } - : opts.filter(k => options[k]).reduce((o, k) => { - o[k] = true - return o - }, {}) -module.exports = parseOptions +const parseOptions = options => { + if (!options) return {}; + + if (typeof options !== 'object') return { loose: true }; + + const parsedOptions = {}; + + // parse out just the options we care about so we always get a consistent + // obj with keys in a consistent order. + + if (options.includePrerelease) parsedOptions.includePrerelease = true; + if (options.loose) parsedOptions.loose = true; + if (options.rtl) parsedOptions.rtl = true; + + return parsedOptions; +}; +module.exports = parseOptions; From 8a20c286afaa3aa2bf45f8c62bf2db16f71f9884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Thu, 30 Mar 2023 22:37:02 -0300 Subject: [PATCH 2/3] perf: faster memoOpts for range --- classes/range.js | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/classes/range.js b/classes/range.js index a791d912..dd6bc8c8 100644 --- a/classes/range.js +++ b/classes/range.js @@ -31,7 +31,7 @@ class Range { this.set = range .split('||') // map the range to a 2d array of comparators - .map(r => this.parseRange(r.trim())) + .map(r => this.parseRange(r)) // throw out any comparator lists that are empty // this generally means that it was not a valid range, which is allowed // in loose mode, but will still throw if the WHOLE range is invalid. @@ -81,8 +81,8 @@ class Range { // memoize range parsing for performance. // this is a very hot path, and fully deterministic. - const memoOpts = Object.keys(this.options).join(',') - const memoKey = `parseRange:${memoOpts}:${range}` + const memoOpts = buildMemoKeyFromOptions(this.options) + const memoKey = memoOpts + range const cached = cache.get(memoKey) if (cached) { return cached @@ -190,6 +190,35 @@ class Range { return false } } + +function buildMemoKeyFromOptions(options) { + if (options.includePrerelease === true) { + if (options.loose === true && options.rtl === true) { + return '1'; + } + + if (options.loose === true) { + return '2'; + } + + if (options.rtl === true) { + return '3'; + } + + return '4'; + } else if (options.loose === true) { + if (options.rtl === true) { + return '5'; + } + + return '6'; + } else if (options.rtl === true) { + return '7'; + } else { + return '8'; + } +} + module.exports = Range const LRU = require('lru-cache') From 665eee4fb74cb1fa9d5c0eea64d988c98ca9eed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Fri, 31 Mar 2023 14:14:34 -0300 Subject: [PATCH 3/3] perf: freeze parse options to avoid object allocation --- internal/parse-options.js | 42 ++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/internal/parse-options.js b/internal/parse-options.js index 7cbcf16c..e72e35aa 100644 --- a/internal/parse-options.js +++ b/internal/parse-options.js @@ -1,17 +1,41 @@ +const var1 = Object.freeze({ includePrerelease: true, loose: true, rtl: true }); +const var2 = Object.freeze({ includePrerelease: true, loose: true }); +const var3 = Object.freeze({ includePrerelease: true, rtl: true }); +const var4 = Object.freeze({ includePrerelease: true }); +const var5 = Object.freeze({ loose: true, rtl: true }); +const var6 = Object.freeze({ loose: true }); +const var7 = Object.freeze({ rtl: true }); +const emptyOpts = Object.freeze({}); + const parseOptions = options => { - if (!options) return {}; + if (!options) return emptyOpts; + + if (typeof options !== 'object') return var6; - if (typeof options !== 'object') return { loose: true }; + if (options.includePrerelease) { + if (options.loose && options.rtl) { + return var1; + } - const parsedOptions = {}; + if (options.loose) { + return var2; + } - // parse out just the options we care about so we always get a consistent - // obj with keys in a consistent order. + if (options.rtl) { + return var3; + } - if (options.includePrerelease) parsedOptions.includePrerelease = true; - if (options.loose) parsedOptions.loose = true; - if (options.rtl) parsedOptions.rtl = true; + return var4; + } else if (options.loose) { + if (options.rtl) { + return var5; + } - return parsedOptions; + return var6; + } else if (options.rtl) { + return var7; + } else { + return emptyOpts; + } }; module.exports = parseOptions;