Bug 1517354 - Improve about:memory performance by not using toLocaleString r=njn
authorCameron McCormack <cam@mcc.id.au>
Mon, 07 Jan 2019 02:50:05 +0000
changeset 509770 a52b66841df98457965924c2810e2a6396df896d
parent 509769 2c87990aee062f3008c9e0ab792d8b6ef9c9215e
child 509771 029414a7448afe97b06f3f5a9c96ceaae93c44e3
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs1517354
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1517354 - Improve about:memory performance by not using toLocaleString r=njn Differential Revision: https://phabricator.services.mozilla.com/D15627
toolkit/components/aboutmemory/content/aboutMemory.js
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -1541,18 +1541,96 @@ const kFrac1Style = {
  * Formats an int as a human-readable string.
  *
  * @param aN
  *        The integer to format.
  * @param aOptions
  *        Optional options object.
  * @return A human-readable string representing the int.
  */
-function formatNum(aN, aOptions) {
-  return aN.toLocaleString("en-US", aOptions);
+function formatNum(aN, aOptions = {}) {
+  // Unfortunately toLocaleString is slow, so we implement a restricted
+  // version of it that just handles the formatting options we need in the
+  // above kFooStyle options objects.  If toLocaleString becomes faster in the
+  // future, this code can be replaced with:
+  //
+  //   return aN.toLocaleString("en-US", aOptions);
+
+  if (Number.isNaN(aN)) {
+    return "NaN";
+  }
+
+  if (aN == Infinity) {
+    return "∞";
+  }
+
+  if (aN == -Infinity) {
+    return "-∞";
+  }
+
+  // Extract options and apply defaults.
+  let style = aOptions.style || "decimal";
+  let percent = style == "percent";
+  let minIntegerDigits = aOptions.minimumIntegerDigits || 1;
+  let minFractionDigits = aOptions.minimumFractionDigits || 0;
+  let maxFractionDigits = aOptions.maximumFractionDigits || (percent ? 1 : 3);
+  assert(style == "decimal" || style == "percent", "unsupported style value");
+
+  // Store the sign and work on the absolute number from here on.
+  let formattedNum = aN < 0 ? "-" : "";
+
+  // Delegate most of the number formatting work to toFixed and then work
+  // on the resulting string.
+  let fixedNum = Math.abs(aN * (percent ? 100 : 1)).toFixed(maxFractionDigits);
+
+  // Split the fixed precision number into its integer and fractional parts.
+  let decimalPointIndex = fixedNum.indexOf(".");
+  let integerPart;
+  let fractionalPart;
+  if (decimalPointIndex == -1) {
+    integerPart = fixedNum;
+    fractionalPart = "";
+  } else {
+    integerPart = fixedNum.substring(0, decimalPointIndex);
+    fractionalPart = fixedNum.substring(decimalPointIndex + 1);
+  }
+
+  // Apply minimum and maximum digit lengths.
+  integerPart = integerPart.padStart(minIntegerDigits, "0");
+  fractionalPart = fractionalPart.padEnd(minFractionDigits, "0");
+
+  let zeroIndex = fractionalPart.length;
+  while (zeroIndex > minFractionDigits &&
+         fractionalPart[zeroIndex - 1] == "0") {
+    --zeroIndex;
+  }
+  fractionalPart = fractionalPart.substring(0, zeroIndex);
+
+  // Insert grouping separators in the integer part.
+  let i = (integerPart.length % 3) || 3;
+  let groupedIntegerPart = integerPart.substring(0, i);
+  while (i < integerPart.length) {
+    groupedIntegerPart += ",";
+    groupedIntegerPart += integerPart.substring(i, i + 3);
+    i += 3;
+  }
+
+  // Construct the formatted number.
+  formattedNum += groupedIntegerPart;
+  if (fractionalPart != "") {
+    formattedNum += ".";
+    formattedNum += fractionalPart;
+  }
+
+  // Add suffix if needed.
+  if (percent) {
+    formattedNum += "%";
+  }
+
+  return formattedNum;
 }
 
 /**
  * Converts a byte count to an appropriate string representation.
  *
  * @param aBytes
  *        The byte count.
  * @return The string representation.