From 9e2011b3ee689ccb0d11fe207fbbff63c5beb303 Mon Sep 17 00:00:00 2001 From: nodify-at <21654050+nodify-at@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:06:40 +0200 Subject: [PATCH 1/4] feat: optimize performance by adding caching for resolved options and parsed locale strings - Implement caching to avoid redundant computations of resolved options and locale parsing. - Introduce a final `isEnglish` variable to efficiently check if the locale supports English. Signed-off-by: nodify-at <21654050+nodify-at@users.noreply.github.com> --- src/impl/locale.js | 68 ++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/src/impl/locale.js b/src/impl/locale.js index 93c489b07..c17e8c4f1 100644 --- a/src/impl/locale.js +++ b/src/impl/locale.js @@ -61,6 +61,14 @@ function systemLocale() { } } +let resolvedOptionsCache = {}; +function resolvedOptions(locString) { + if (!resolvedOptionsCache[locString]) { + resolvedOptionsCache[locString] = new Intl.DateTimeFormat(locString).resolvedOptions(); + } + return resolvedOptionsCache[locString]; +} + let weekInfoCache = {}; function getCachedWeekInfo(locString) { let data = weekInfoCache[locString]; @@ -73,7 +81,18 @@ function getCachedWeekInfo(locString) { return data; } -function parseLocaleString(localeStr) { +const localeStringCache = {}; +function getParsedCachedLocaleString(localeStr) { + if (localeStringCache[localeStr]) { + return localeStringCache[localeStr]; + } + + const result = _parseLocaleString(localeStr); + localeStringCache[localeStr] = result; + return result; +} + +function _parseLocaleString(localeStr) { // I really want to avoid writing a BCP 47 parser // see, e.g. https://github.com/wooorm/bcp-47 // Instead, we'll do this: @@ -110,7 +129,13 @@ function parseLocaleString(localeStr) { } } +const intlConfigCache = {}; function intlConfigString(localeStr, numberingSystem, outputCalendar) { + const key = `${localeStr}${numberingSystem}${outputCalendar}`; + if (intlConfigCache[key]) { + return intlConfigCache[key]; + } + if (outputCalendar || numberingSystem) { if (!localeStr.includes("-u-")) { localeStr += "-u"; @@ -123,10 +148,9 @@ function intlConfigString(localeStr, numberingSystem, outputCalendar) { if (numberingSystem) { localeStr += `-nu-${numberingSystem}`; } - return localeStr; - } else { - return localeStr; } + intlConfigCache[key] = localeStr; + return localeStr; } function mapMonths(f) { @@ -160,16 +184,12 @@ function listStuff(loc, length, englishFn, intlFn) { } function supportsFastNumbers(loc) { - if (loc.numberingSystem && loc.numberingSystem !== "latn") { - return false; - } else { - return ( - loc.numberingSystem === "latn" || - !loc.locale || - loc.locale.startsWith("en") || - new Intl.DateTimeFormat(loc.intl).resolvedOptions().numberingSystem === "latn" - ); - } + return loc.numberingSystem && loc.numberingSystem !== "latn" + ? false + : loc.numberingSystem === "latn" || + !loc.locale || + loc.locale.startsWith("en") || + resolvedOptions(loc.locale).numberingSystem === "latn"; } /** @@ -326,7 +346,6 @@ const fallbackWeekSettings = { /** * @private */ - export default class Locale { static fromOpts(opts) { return Locale.create( @@ -360,7 +379,8 @@ export default class Locale { } constructor(locale, numbering, outputCalendar, weekSettings, specifiedLocale) { - const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale); + const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = + getParsedCachedLocaleString(locale); this.locale = parsedLocale; this.numberingSystem = numbering || parsedNumberingSystem || null; @@ -374,14 +394,14 @@ export default class Locale { this.eraCache = {}; this.specifiedLocale = specifiedLocale; - this.fastNumbersCached = null; + this.fastNumbersCached = supportsFastNumbers(this); + this.isEnglishCached = + this.locale === "en" || + this.locale.toLowerCase() === "en-us" || + resolvedOptions(this.locale).locale.startsWith("en-us"); } get fastNumbers() { - if (this.fastNumbersCached == null) { - this.fastNumbersCached = supportsFastNumbers(this); - } - return this.fastNumbersCached; } @@ -503,11 +523,7 @@ export default class Locale { } isEnglish() { - return ( - this.locale === "en" || - this.locale.toLowerCase() === "en-us" || - new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith("en-us") - ); + return this.isEnglishCached; } getWeekSettings() { From a2f6f6900775e70354c5e1ec0b5e82a92df52961 Mon Sep 17 00:00:00 2001 From: nodify-at <21654050+nodify-at@users.noreply.github.com> Date: Sun, 4 Aug 2024 17:24:21 +0200 Subject: [PATCH 2/4] feat: simplify caching logic for resolved options in supportsFastNumbers and isEnglish calls to improve performance Signed-off-by: nodify-at <21654050+nodify-at@users.noreply.github.com> --- src/impl/locale.js | 61 ++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/src/impl/locale.js b/src/impl/locale.js index c17e8c4f1..d129bb2b7 100644 --- a/src/impl/locale.js +++ b/src/impl/locale.js @@ -62,7 +62,7 @@ function systemLocale() { } let resolvedOptionsCache = {}; -function resolvedOptions(locString) { +function getCachedIntResolvedOptions(locString) { if (!resolvedOptionsCache[locString]) { resolvedOptionsCache[locString] = new Intl.DateTimeFormat(locString).resolvedOptions(); } @@ -81,18 +81,7 @@ function getCachedWeekInfo(locString) { return data; } -const localeStringCache = {}; -function getParsedCachedLocaleString(localeStr) { - if (localeStringCache[localeStr]) { - return localeStringCache[localeStr]; - } - - const result = _parseLocaleString(localeStr); - localeStringCache[localeStr] = result; - return result; -} - -function _parseLocaleString(localeStr) { +function parseLocaleString(localeStr) { // I really want to avoid writing a BCP 47 parser // see, e.g. https://github.com/wooorm/bcp-47 // Instead, we'll do this: @@ -129,13 +118,7 @@ function _parseLocaleString(localeStr) { } } -const intlConfigCache = {}; function intlConfigString(localeStr, numberingSystem, outputCalendar) { - const key = `${localeStr}${numberingSystem}${outputCalendar}`; - if (intlConfigCache[key]) { - return intlConfigCache[key]; - } - if (outputCalendar || numberingSystem) { if (!localeStr.includes("-u-")) { localeStr += "-u"; @@ -148,9 +131,10 @@ function intlConfigString(localeStr, numberingSystem, outputCalendar) { if (numberingSystem) { localeStr += `-nu-${numberingSystem}`; } + return localeStr; + } else { + return localeStr; } - intlConfigCache[key] = localeStr; - return localeStr; } function mapMonths(f) { @@ -184,12 +168,16 @@ function listStuff(loc, length, englishFn, intlFn) { } function supportsFastNumbers(loc) { - return loc.numberingSystem && loc.numberingSystem !== "latn" - ? false - : loc.numberingSystem === "latn" || - !loc.locale || - loc.locale.startsWith("en") || - resolvedOptions(loc.locale).numberingSystem === "latn"; + if (loc.numberingSystem && loc.numberingSystem !== "latn") { + return false; + } else { + return ( + loc.numberingSystem === "latn" || + !loc.locale || + loc.locale.startsWith("en") || + getCachedIntResolvedOptions(loc.locale).numberingSystem === "latn" + ); + } } /** @@ -379,8 +367,7 @@ export default class Locale { } constructor(locale, numbering, outputCalendar, weekSettings, specifiedLocale) { - const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = - getParsedCachedLocaleString(locale); + const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale); this.locale = parsedLocale; this.numberingSystem = numbering || parsedNumberingSystem || null; @@ -394,14 +381,14 @@ export default class Locale { this.eraCache = {}; this.specifiedLocale = specifiedLocale; - this.fastNumbersCached = supportsFastNumbers(this); - this.isEnglishCached = - this.locale === "en" || - this.locale.toLowerCase() === "en-us" || - resolvedOptions(this.locale).locale.startsWith("en-us"); + this.fastNumbersCached = null; } get fastNumbers() { + if (this.fastNumbersCached == null) { + this.fastNumbersCached = supportsFastNumbers(this); + } + return this.fastNumbersCached; } @@ -523,7 +510,11 @@ export default class Locale { } isEnglish() { - return this.isEnglishCached; + return ( + this.locale === "en" || + this.locale.toLowerCase() === "en-us" || + getCachedIntResolvedOptions(this.intl).locale.startsWith("en-us") + ); } getWeekSettings() { From 456325e24939db6e93ae2e2b72ddced7e6cecaa3 Mon Sep 17 00:00:00 2001 From: nodify-at <21654050+nodify-at@users.noreply.github.com> Date: Sun, 4 Aug 2024 21:41:28 +0200 Subject: [PATCH 3/4] feat: add new benchmark tests to compare performance with and without Intl.resolvedOptions cache Signed-off-by: nodify-at <21654050+nodify-at@users.noreply.github.com> --- benchmarks/datetime.js | 7 +++++++ src/impl/locale.js | 9 +++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/benchmarks/datetime.js b/benchmarks/datetime.js index f4dffda7b..aef549810 100644 --- a/benchmarks/datetime.js +++ b/benchmarks/datetime.js @@ -58,6 +58,13 @@ function runDateTimeSuite() { dt.toFormat("T"); Settings.resetCaches(); }) + .add("DateTime#format in german", () => { + dt.setLocale("de-De").toFormat("d. LLL. HH:mm"); + }) + .add("DateTime#format in german and no-cache", () => { + dt.setLocale("de-De").toFormat("d. LLL. HH:mm"); + Settings.resetCaches(); + }) .add("DateTime#add", () => { dt.plus({ milliseconds: 3434 }); }) diff --git a/src/impl/locale.js b/src/impl/locale.js index d129bb2b7..b0ecc8c08 100644 --- a/src/impl/locale.js +++ b/src/impl/locale.js @@ -61,12 +61,12 @@ function systemLocale() { } } -let resolvedOptionsCache = {}; +let intlResolvedOptionsCache = {}; function getCachedIntResolvedOptions(locString) { - if (!resolvedOptionsCache[locString]) { - resolvedOptionsCache[locString] = new Intl.DateTimeFormat(locString).resolvedOptions(); + if (!intlResolvedOptionsCache[locString]) { + intlResolvedOptionsCache[locString] = new Intl.DateTimeFormat(locString).resolvedOptions(); } - return resolvedOptionsCache[locString]; + return intlResolvedOptionsCache[locString]; } let weekInfoCache = {}; @@ -360,6 +360,7 @@ export default class Locale { intlDTCache = {}; intlNumCache = {}; intlRelCache = {}; + intlResolvedOptionsCache = {}; } static fromObject({ locale, numberingSystem, outputCalendar, weekSettings } = {}) { From c4aee302c3b0f010b075731c58edd8f0b111a90f Mon Sep 17 00:00:00 2001 From: nodify-at <21654050+nodify-at@users.noreply.github.com> Date: Thu, 12 Sep 2024 00:35:29 +0200 Subject: [PATCH 4/4] Fixed parameter to check cached resolved options --- src/impl/locale.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl/locale.js b/src/impl/locale.js index b0ecc8c08..d66c9c747 100644 --- a/src/impl/locale.js +++ b/src/impl/locale.js @@ -175,7 +175,7 @@ function supportsFastNumbers(loc) { loc.numberingSystem === "latn" || !loc.locale || loc.locale.startsWith("en") || - getCachedIntResolvedOptions(loc.locale).numberingSystem === "latn" + getCachedIntResolvedOptions(loc.intl).numberingSystem === "latn" ); } }