summaryrefslogtreecommitdiffstats
path: root/intl/benchmarks
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /intl/benchmarks
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'intl/benchmarks')
-rw-r--r--intl/benchmarks/README.md20
-rw-r--r--intl/benchmarks/head.js131
-rw-r--r--intl/benchmarks/perftest.ini4
-rw-r--r--intl/benchmarks/perftest_dateTimeFormat.js122
-rw-r--r--intl/benchmarks/perftest_locale.js162
-rw-r--r--intl/benchmarks/perftest_numberFormat.js221
-rw-r--r--intl/benchmarks/perftest_pluralRules.js119
-rw-r--r--intl/benchmarks/xpcshell.ini17
8 files changed, 796 insertions, 0 deletions
diff --git a/intl/benchmarks/README.md b/intl/benchmarks/README.md
new file mode 100644
index 0000000000..386cd1f182
--- /dev/null
+++ b/intl/benchmarks/README.md
@@ -0,0 +1,20 @@
+# Intl Performance Microbenchmarks
+
+This folder contains micro benchmarks using the [mozperftest][] suite.
+
+[mozperftest](https://firefox-source-docs.mozilla.org/testing/perfdocs/mozperftest.html)
+
+## Recording profiles for the Firefox Profiler
+
+```sh
+# Run the perftest as an xpcshell test.
+MOZ_PROFILER_STARTUP=1 \
+ MOZ_PROFILER_SHUTDOWN=/path/to/perf-profile.json \
+ ./mach xpcshell-test intl/benchmarks/perftest_dateTimeFormat.js
+
+# Install the profiler-symbol-server tool.
+cargo install profiler-symbol-server
+
+# Open the path to the file.
+profiler-symbol-server --open /path/to/perf-profile.json
+```
diff --git a/intl/benchmarks/head.js b/intl/benchmarks/head.js
new file mode 100644
index 0000000000..96250484a8
--- /dev/null
+++ b/intl/benchmarks/head.js
@@ -0,0 +1,131 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Create an interface to measure iterations for a micro benchmark. These iterations
+ * will then be reported to the perftest runner.
+ *
+ * @param {string} metricName
+ */
+function measureIterations(metricName) {
+ let accumulatedTime = 0;
+ let iterations = 0;
+ let now = 0;
+ return {
+ /**
+ * Start a measurement.
+ */
+ start() {
+ now = Cu.now();
+ },
+ /**
+ * Stop a measurement, and record the elapsed time.
+ */
+ stop() {
+ accumulatedTime += Cu.now() - now;
+ iterations++;
+ },
+ /**
+ * Report the metrics to perftest after finishing the microbenchmark.
+ */
+ reportMetrics() {
+ const metrics = {};
+ metrics[metricName + " iterations"] = iterations;
+ metrics[metricName + " accumulatedTime"] = accumulatedTime;
+ metrics[metricName + " perCallTime"] = accumulatedTime / iterations;
+
+ info("perfMetrics", metrics);
+ },
+ };
+}
+
+let _seed = 123456;
+/**
+ * A cheap and simple pseudo-random number generator that avoids adding new dependencies.
+ * This function ensures tests are repeatable, but can be fed random configurations.
+ *
+ * https://en.wikipedia.org/wiki/Linear_congruential_generator
+ *
+ * It has the following distribution for the first 100,000 runs:
+ *
+ * 0.0 - 0.1: 9948
+ * 0.1 - 0.2: 10037
+ * 0.2 - 0.3: 10049
+ * 0.3 - 0.4: 10041
+ * 0.4 - 0.5: 10036
+ * 0.5 - 0.6: 10085
+ * 0.6 - 0.7: 9987
+ * 0.7 - 0.8: 9872
+ * 0.8 - 0.9: 10007
+ * 0.9 - 1.0: 9938
+ *
+ * @returns {number} float values ranged 0-1
+ */
+function prng() {
+ _seed = Math.imul(_seed, 22695477) + 1;
+ return (_seed >> 1) / 0x7fffffff + 0.5;
+}
+
+/**
+ * The distribution of locales. The number represents the ratio of total users in that
+ * locale. The numbers should add up to ~1.0.
+ *
+ * https://sql.telemetry.mozilla.org/dashboard/firefox-localization
+ */
+const localeDistribution = {
+ "en-US": 0.373,
+ de: 0.129,
+ fr: 0.084,
+ "zh-CN": 0.053,
+ ru: 0.048,
+ "es-ES": 0.047,
+ pl: 0.041,
+ "pt-BR": 0.034,
+ it: 0.028,
+ "en-GB": 0.027,
+ ja: 0.019,
+ "es-MX": 0.014,
+ nl: 0.01,
+ cs: 0.009,
+ hu: 0.008,
+ id: 0.006,
+ "en-CA": 0.006,
+ "es-AR": 0.006,
+ tr: 0.005,
+ el: 0.005,
+ "zh-TW": 0.005,
+ fi: 0.005,
+ "sv-SE": 0.004,
+ "pt-PT": 0.004,
+ sk: 0.003,
+ ar: 0.003,
+ vi: 0.003,
+ "es-CL": 0.002,
+ th: 0.002,
+ da: 0.002,
+ bg: 0.002,
+ ro: 0.002,
+ "nb-NO": 0.002,
+ ko: 0.002,
+};
+
+/**
+ * Go through the top Firefox locales, and pick one at random that is representative
+ * of the Firefox population as of 2021-06-03. It uses a pseudo-random number generator
+ * to make the results repeatable.
+ *
+ * @returns {string} locale
+ */
+function pickRepresentativeLocale() {
+ const n = prng();
+ let ratio = 1;
+ for (const [locale, representation] of Object.entries(localeDistribution)) {
+ ratio -= representation;
+ if (n > ratio) {
+ return locale;
+ }
+ }
+ // In case we fall through the "for" loop, return the most common locale.
+ return "en-US";
+}
diff --git a/intl/benchmarks/perftest.ini b/intl/benchmarks/perftest.ini
new file mode 100644
index 0000000000..390c0636c8
--- /dev/null
+++ b/intl/benchmarks/perftest.ini
@@ -0,0 +1,4 @@
+[perftest_dateTimeFormat.js]
+[perftest_locale.js]
+[perftest_numberFormat.js]
+[perftest_pluralRules.js]
diff --git a/intl/benchmarks/perftest_dateTimeFormat.js b/intl/benchmarks/perftest_dateTimeFormat.js
new file mode 100644
index 0000000000..ffb75d0464
--- /dev/null
+++ b/intl/benchmarks/perftest_dateTimeFormat.js
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// @ts-check
+
+var perfMetadata = {
+ owner: "Internationalization Team",
+ name: "Intl.DateTimeFormat",
+ description: "Test the speed of the Intl.DateTimeFormat implementation.",
+ options: {
+ default: {
+ perfherder: true,
+ perfherder_metrics: [
+ {
+ name: "Intl.DateTimeFormat constructor iterations",
+ unit: "iterations",
+ },
+ { name: "Intl.DateTimeFormat constructor accumulatedTime", unit: "ms" },
+ { name: "Intl.DateTimeFormat constructor perCallTime", unit: "ms" },
+
+ {
+ name: "Intl.DateTimeFormat.prototype.format iterations",
+ unit: "iterations",
+ },
+ {
+ name: "Intl.DateTimeFormat.prototype.format accumulatedTime",
+ unit: "ms",
+ },
+ {
+ name: "Intl.DateTimeFormat.prototype.format perCallTime",
+ unit: "ms",
+ },
+ ],
+ verbose: true,
+ },
+ },
+ tags: ["intl", "ecma402"],
+};
+
+add_task(function measure_date() {
+ const measureConstructor = measureIterations(
+ "Intl.DateTimeFormat constructor"
+ );
+ const measureFormat = measureIterations(
+ "Intl.DateTimeFormat.prototype.format"
+ );
+
+ // Re-use the config between runs.
+
+ const fieldOptions = {
+ weekday: ["narrow", "short", "long"],
+ era: ["narrow", "short", "long"],
+ year: ["2-digit", "numeric"],
+ month: ["2-digit", "numeric", "narrow", "short", "long"],
+ day: ["2-digit", "numeric"],
+ hour: ["2-digit", "numeric"],
+ minute: ["2-digit", "numeric"],
+ second: ["2-digit", "numeric"],
+ timeZoneName: ["short", "long"],
+ };
+
+ const config = {};
+ function randomizeConfig(name, chance) {
+ const option = fieldOptions[name];
+ if (prng() < chance) {
+ config[name] = option[Math.floor(option.length * prng())];
+ } else {
+ delete config[name];
+ }
+ }
+
+ let date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
+
+ // Split each step of the benchmark into separate JS functions so that performance
+ // profiles are easy to analyze.
+
+ function benchmarkDateTimeFormatConstructor() {
+ for (let i = 0; i < 1000; i++) {
+ // Create a random configuration powered by a pseudo-random number generator. This
+ // way the configurations will be the same between 2 different runs.
+ const locale = pickRepresentativeLocale();
+ randomizeConfig("year", 0.5);
+ randomizeConfig("month", 0.5);
+ randomizeConfig("day", 0.5);
+ randomizeConfig("hour", 0.5);
+ randomizeConfig("minute", 0.5);
+ // Set the following to some lower probabilities:
+ randomizeConfig("second", 0.2);
+ randomizeConfig("timeZoneName", 0.2);
+ randomizeConfig("weekday", 0.2);
+ randomizeConfig("era", 0.1);
+
+ // Measure the constructor.
+ measureConstructor.start();
+ const formatter = Intl.DateTimeFormat(locale, config);
+ // Also include one format operation to ensure the constructor is de-lazified.
+ formatter.format(date);
+ measureConstructor.stop();
+
+ benchmarkFormatOperation(formatter);
+ }
+ }
+
+ const start = Date.UTC(2000);
+ const end = Date.UTC(2030);
+ const dateDiff = end - start;
+ function benchmarkFormatOperation(formatter) {
+ // Measure the format operation.
+ for (let j = 0; j < 100; j++) {
+ date = new Date(start + prng() * dateDiff);
+ measureFormat.start();
+ formatter.format(date);
+ measureFormat.stop();
+ }
+ }
+
+ benchmarkDateTimeFormatConstructor();
+ measureConstructor.reportMetrics();
+ measureFormat.reportMetrics();
+
+ ok(true);
+});
diff --git a/intl/benchmarks/perftest_locale.js b/intl/benchmarks/perftest_locale.js
new file mode 100644
index 0000000000..aac624127c
--- /dev/null
+++ b/intl/benchmarks/perftest_locale.js
@@ -0,0 +1,162 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// @ts-check
+
+var perfMetadata = {
+ owner: "Internationalization Team",
+ name: "Intl.Locale",
+ description: "Test the speed of the Intl.Locale implementation.",
+ options: {
+ default: {
+ perfherder: true,
+ perfherder_metrics: [
+ {
+ name: "Intl.Locale constructor iterations",
+ unit: "iterations",
+ },
+ { name: "Intl.Locale constructor accumulatedTime", unit: "ms" },
+ { name: "Intl.Locale constructor perCallTime", unit: "ms" },
+
+ {
+ name: "Intl.Locale.prototype accessors iterations",
+ unit: "iterations",
+ },
+ {
+ name: "Intl.Locale.prototype accessors accumulatedTime",
+ unit: "ms",
+ },
+ {
+ name: "Intl.Locale.prototype accessors perCallTime",
+ unit: "ms",
+ },
+
+ {
+ name: "Intl.Locale.maximize operation iterations",
+ unit: "iterations",
+ },
+ {
+ name: "Intl.Locale.maximize operation accumulatedTime",
+ unit: "ms",
+ },
+ {
+ name: "Intl.Locale.maximize operation perCallTime",
+ unit: "ms",
+ },
+ ],
+ verbose: true,
+ },
+ },
+ tags: ["intl", "ecma402"],
+};
+
+const maximizeLocales = [
+ "en-US",
+ "en-GB",
+ "es-AR",
+ "it",
+ "zh-Hans-CN",
+ "de-AT",
+ "pl",
+ "fr-FR",
+ "de-AT",
+ "sr-Cyrl-SR",
+ "nb-NO",
+ "fr-FR",
+ "mk",
+ "uk",
+ "und-PL",
+ "und-Latn-AM",
+ "ug-Cyrl",
+ "sr-ME",
+ "mn-Mong",
+ "lif-Limb",
+ "gan",
+ "zh-Hant",
+ "yue-Hans",
+ "unr",
+ "unr-Deva",
+ "und-Thai-CN",
+ "ug-Cyrl",
+ "en-Latn-DE",
+ "pl-FR",
+ "de-CH",
+ "tuq",
+ "sr-ME",
+ "ng",
+ "klx",
+ "kk-Arab",
+ "en-Cyrl",
+ "und-Cyrl-UK",
+ "und-Arab",
+ "und-Arab-FO",
+];
+
+add_task(function measure_locale() {
+ const measureConstructor = measureIterations("Intl.Locale constructor");
+ const measureAccessors = measureIterations("Intl.Locale.prototype accessors");
+ const measureMaximize = measureIterations("Intl.Locale.maximize operation");
+
+ // Split each step of the benchmark into separate JS functions so that performance
+ // profiles are easy to analyze.
+
+ function benchmarkDateTimeFormatConstructor() {
+ for (let i = 0; i < 1000; i++) {
+ // Create a random configuration powered by a pseudo-random number generator. This
+ // way the configurations will be the same between 2 different runs.
+ const localeString = pickRepresentativeLocale();
+
+ // Measure the constructor.
+ measureConstructor.start();
+ const locale = new Intl.Locale(localeString);
+ measureConstructor.stop();
+
+ benchmarkAccessors(locale);
+ }
+ }
+
+ const accessors = [
+ "basename",
+ "calendar",
+ "caseFirst",
+ "collation",
+ "hourCycle",
+ "numeric",
+ "numberingSystem",
+ "language",
+ "script",
+ "region",
+ ];
+
+ function benchmarkAccessors(locale) {
+ for (let j = 0; j < 100; j++) {
+ measureAccessors.start();
+ for (let accessor in accessors) {
+ locale[accessor];
+ }
+ measureAccessors.stop();
+ }
+ }
+
+ function benchmarkMaximize() {
+ let locales = [];
+ for (let localeString of maximizeLocales) {
+ locales.push(new Intl.Locale(localeString));
+ }
+ for (let j = 0; j < 10000; j++) {
+ measureMaximize.start();
+ for (let locale of locales) {
+ locale.maximize();
+ }
+ measureMaximize.stop();
+ }
+ }
+
+ benchmarkDateTimeFormatConstructor();
+ benchmarkMaximize();
+ measureConstructor.reportMetrics();
+ measureAccessors.reportMetrics();
+ measureMaximize.reportMetrics();
+
+ ok(true);
+});
diff --git a/intl/benchmarks/perftest_numberFormat.js b/intl/benchmarks/perftest_numberFormat.js
new file mode 100644
index 0000000000..c2a254c6ab
--- /dev/null
+++ b/intl/benchmarks/perftest_numberFormat.js
@@ -0,0 +1,221 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// @ts-check
+
+var perfMetadata = {
+ owner: "Internationalization Team",
+ name: "Intl.NumberFormat",
+ description: "Test the speed of the Intl.NumberFormat implementation.",
+ options: {
+ default: {
+ perfherder: true,
+ perfherder_metrics: [
+ {
+ name: "Intl.NumberFormat constructor iterations",
+ unit: "iterations",
+ },
+ { name: "Intl.NumberFormat constructor accumulatedTime", unit: "ms" },
+ { name: "Intl.NumberFormat constructor perCallTime", unit: "ms" },
+
+ {
+ name: "Intl.NumberFormat.prototype.format iterations",
+ unit: "iterations",
+ },
+ {
+ name: "Intl.NumberFormat.prototype.format accumulatedTime",
+ unit: "ms",
+ },
+ {
+ name: "Intl.NumberFormat.prototype.format perCallTime",
+ unit: "ms",
+ },
+
+ {
+ name: "Intl.NumberFormat.prototype.formatToParts iterations",
+ unit: "iterations",
+ },
+ {
+ name: "Intl.NumberFormat.prototype.formatToParts accumulatedTime",
+ unit: "ms",
+ },
+ {
+ name: "Intl.NumberFormat.prototype.formatToParts perCallTime",
+ unit: "ms",
+ },
+ ],
+ verbose: true,
+ },
+ },
+ tags: ["intl", "ecma402"],
+};
+
+add_task(function measure_numberformat() {
+ const measureConstructor = measureIterations("Intl.NumberFormat constructor");
+ const measureFormat = measureIterations("Intl.NumberFormat.prototype.format");
+ const measureFormatToParts = measureIterations(
+ "Intl.NumberFormat.prototype.formatToParts"
+ );
+
+ // Re-use the config between runs.
+
+ const styles = ["decimal", "percent", "currency", "unit"];
+
+ const numberStyles = [
+ "arab",
+ "arabext",
+ "bali",
+ "beng",
+ "deva",
+ "fullwide",
+ "gujr",
+ "guru",
+ "hanidec",
+ "khmr",
+ "knda",
+ "laoo",
+ "latn",
+ "limb",
+ "mlym",
+ "mong",
+ "mymr",
+ "orya",
+ "tamldec",
+ "telu",
+ "thai",
+ "tibt",
+ ];
+
+ const decimalOptions = {
+ notation: ["scientific", "engineering", "compact"],
+ useGroup: [true, false],
+ };
+
+ const currencyOptions = {
+ currency: ["USD", "CAD", "EUR", "Yen", "MXN", "SAR", "INR", "CNY", "IDR"],
+ currencyDisplay: ["symbol", "narrowSymbol", "code", "name"],
+ currencySign: ["accounting", "standard"],
+ };
+
+ const unitOptions = {
+ unit: [
+ "acre",
+ "bit",
+ "byte",
+ "celsius",
+ "centimeter",
+ "day",
+ "degree",
+ "fahrenheit",
+ "fluid-ounce",
+ "foot",
+ "gallon",
+ "gigabit",
+ "gigabyte",
+ "gram",
+ "hectare",
+ "hour",
+ "inch",
+ "kilobit",
+ "kilobyte",
+ "kilogram",
+ "kilometer",
+ "liter",
+ "megabit",
+ "megabyte",
+ "meter",
+ "mile",
+ "mile-scandinavian",
+ "milliliter",
+ "millimeter",
+ "millisecond",
+ "minute",
+ "month",
+ "ounce",
+ "percent",
+ "petabyte",
+ "pound",
+ "second",
+ "stone",
+ "terabit",
+ "terabyte",
+ "week",
+ "yard",
+ "year",
+ "meter-per-second",
+ "kilometer-per-hour",
+ ],
+ unitDisplay: ["long", "short", "narrow"],
+ };
+
+ function choose(options) {
+ return options[Math.floor(options.length * prng())];
+ }
+
+ function randomizeConfig(config, options) {
+ for (let option in options) {
+ config[option] = choose(options[option]);
+ }
+ }
+
+ // Split each step of the benchmark into separate JS functions so that performance
+ // profiles are easy to analyze.
+
+ function benchmarkNumberFormatConstructor() {
+ for (let i = 0; i < 1000; i++) {
+ // Create a random configuration powered by a pseudo-random number generator. This
+ // way the configurations will be the same between 2 different runs.
+ const locale = pickRepresentativeLocale();
+ const style = choose(styles);
+ const nu = choose(numberStyles);
+ let config = {
+ style,
+ nu,
+ };
+ if (style == "decimal") {
+ randomizeConfig(config, decimalOptions);
+ } else if (style == "currency") {
+ randomizeConfig(config, currencyOptions);
+ } else if (style == "unit") {
+ randomizeConfig(config, unitOptions);
+ }
+
+ // Measure the constructor.
+ measureConstructor.start();
+ const formatter = Intl.NumberFormat(locale, config);
+ // Also include one format operation to ensure the constructor is de-lazified.
+ formatter.format(0);
+ measureConstructor.stop();
+
+ benchmarkFormatOperation(formatter);
+ benchmarkFormatToPartsOperation(formatter);
+ }
+ }
+
+ function benchmarkFormatOperation(formatter) {
+ // Measure the format operation.
+ for (let j = 0; j < 100; j++) {
+ let num = -1e6 + prng() * 2e6;
+ measureFormat.start();
+ formatter.format(num);
+ measureFormat.stop();
+ }
+ }
+
+ function benchmarkFormatToPartsOperation(formatter) {
+ // Measure the formatToParts operation.
+ for (let j = 0; j < 100; j++) {
+ let num = -1e6 + prng() * 2e6;
+ measureFormatToParts.start();
+ formatter.formatToParts(num);
+ measureFormatToParts.stop();
+ }
+ }
+
+ benchmarkNumberFormatConstructor();
+ measureConstructor.reportMetrics();
+ measureFormat.reportMetrics();
+ measureFormatToParts.reportMetrics();
+
+ ok(true);
+});
diff --git a/intl/benchmarks/perftest_pluralRules.js b/intl/benchmarks/perftest_pluralRules.js
new file mode 100644
index 0000000000..956dd09561
--- /dev/null
+++ b/intl/benchmarks/perftest_pluralRules.js
@@ -0,0 +1,119 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// @ts-check
+
+var perfMetadata = {
+ owner: "Internationalization Team",
+ name: "Intl.PluralRules",
+ description: "Test the speed of the Intl.PluralRules implementation.",
+ options: {
+ default: {
+ perfherder: true,
+ perfherder_metrics: [
+ {
+ name: "Intl.PluralRules constructor iterations",
+ unit: "iterations",
+ },
+ { name: "Intl.PluralRules constructor accumulatedTime", unit: "ms" },
+ { name: "Intl.PluralRules constructor perCallTime", unit: "ms" },
+
+ {
+ name: "Intl.PluralRules.prototype.select iterations",
+ unit: "iterations",
+ },
+ {
+ name: "Intl.PluralRules.prototype.select accumulatedTime",
+ unit: "ms",
+ },
+ {
+ name: "Intl.PluralRules.prototype.select perCallTime",
+ unit: "ms",
+ },
+
+ {
+ name: "Intl.PluralRules pluralCategories iterations",
+ unit: "iterations",
+ },
+ {
+ name: "Intl.PluralRules pluralCategories accumulatedTime",
+ unit: "ms",
+ },
+ {
+ name: "Intl.PluralRules pluralCategories perCallTime",
+ unit: "ms",
+ },
+ ],
+ verbose: true,
+ },
+ },
+ tags: ["intl", "ecma402"],
+};
+
+add_task(function measure_pluralrules() {
+ const measureConstructor = measureIterations("Intl.PluralRules constructor");
+ const measureSelect = measureIterations("Intl.PluralRules.prototype.select");
+ const measurePluralCategories = measureIterations(
+ "Intl.PluralRules pluralCategories"
+ );
+
+ // Re-use the config between runs.
+
+ const fieldOptions = {
+ type: ["cardinal", "ordinal"],
+ };
+
+ const config = {};
+ function randomizeConfig(name, chance) {
+ const option = fieldOptions[name];
+ if (prng() < chance) {
+ config[name] = option[Math.floor(option.length * prng())];
+ } else {
+ delete config[name];
+ }
+ }
+
+ // Split each step of the benchmark into separate JS functions so that performance
+ // profiles are easy to analyze.
+
+ function benchmarkPluralRulesConstructor() {
+ for (let i = 0; i < 1000; i++) {
+ // Create a random configuration powered by a pseudo-random number generator. This
+ // way the configurations will be the same between 2 different runs.
+ const locale = pickRepresentativeLocale();
+ randomizeConfig("type", 0.5);
+
+ // Measure the constructor.
+ measureConstructor.start();
+ const pr = new Intl.PluralRules(locale, config);
+ measureConstructor.stop();
+
+ benchmarkSelectOperation(pr);
+ benchmarkPluralCategories(pr);
+ }
+ }
+
+ function benchmarkSelectOperation(pr) {
+ // Measure the select operation.
+ for (let j = 0; j < 1000; j++) {
+ // TODO: We may want to extend this to non-integer values in the future.
+ const num = Math.floor(prng() * 10000);
+ measureSelect.start();
+ pr.select(num);
+ measureSelect.stop();
+ }
+ }
+
+ function benchmarkPluralCategories(pr) {
+ measurePluralCategories.start();
+ pr.resolvedOptions().pluralCategories;
+ measurePluralCategories.stop();
+ }
+
+ benchmarkPluralRulesConstructor();
+ measureConstructor.reportMetrics();
+ measureSelect.reportMetrics();
+ measurePluralCategories.reportMetrics();
+
+ ok(true);
+});
diff --git a/intl/benchmarks/xpcshell.ini b/intl/benchmarks/xpcshell.ini
new file mode 100644
index 0000000000..6e0935be0e
--- /dev/null
+++ b/intl/benchmarks/xpcshell.ini
@@ -0,0 +1,17 @@
+[DEFAULT]
+head = head.js
+
+# Add perftests here as it's useful to run them as xpcshell tests, but we don't need them
+# to be run in CI.
+
+[perftest_dateTimeFormat.js]
+skip-if = true
+
+[perftest_locale.js]
+skip-if = true
+
+[perftest_numberFormat.js]
+skip-if = true
+
+[perftest_pluralRules.js]
+skip-if = true