[JSC] Optimize Number#toString with Int52
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 25 Jan 2017 02:40:52 +0000 (02:40 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 25 Jan 2017 02:40:52 +0000 (02:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=167303

Reviewed by Sam Weinig.

JSTests:

* stress/to-string-with-int52.js: Added.
(shouldBe):

Source/JavaScriptCore:

In kraken crypto-sha256-iterative, we frequently call Number.prototype.toString with
Int52. In that case, toString handles it in the generic double path. But we should
have a fast path for this since it can be represented in int64_t.

The stanford-crypto-sha256-iterative shows 1.6% performance improvement (on Linux machine hanayamata).

    Collected 100 samples per benchmark/VM, with 100 VM invocations per benchmark. Emitted a call to gc() between
    sample measurements. Used 1 benchmark iteration per VM invocation for warm-up. Used the jsc-specific preciseTime()
    function to get microsecond-level timing. Reporting benchmark execution times with 95% confidence intervals in
    milliseconds.

                                               baseline                  patched

    stanford-crypto-sha256-iterative        32.853+-0.075      ^      32.325+-0.055         ^ definitely 1.0163x faster

* runtime/JSCJSValue.h:
* runtime/NumberPrototype.cpp:
(JSC::int52ToStringWithRadix):
(JSC::toStringWithRadix):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@211128 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/stress/to-string-with-int52.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/JSCJSValue.h
Source/JavaScriptCore/runtime/NumberPrototype.cpp

index f3acbd5..9fecb3f 100644 (file)
@@ -1,3 +1,13 @@
+2017-01-24  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Optimize Number#toString with Int52
+        https://bugs.webkit.org/show_bug.cgi?id=167303
+
+        Reviewed by Sam Weinig.
+
+        * stress/to-string-with-int52.js: Added.
+        (shouldBe):
+
 2017-01-24  Filip Pizlo  <fpizlo@apple.com>
 
         Atomics.store should return the int-converted value, not the value that it stored
diff --git a/JSTests/stress/to-string-with-int52.js b/JSTests/stress/to-string-with-int52.js
new file mode 100644 (file)
index 0000000..c7fe985
--- /dev/null
@@ -0,0 +1,15 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+shouldBe((0xfffffffffff).toString(16), `fffffffffff`);
+shouldBe((-0xfffffffffff).toString(16), `-fffffffffff`);
+shouldBe((0xfffffffffff000).toString(16), `fffffffffff000`);
+shouldBe((-0xfffffffffff000).toString(16), `-fffffffffff000`);
+
+shouldBe((0x8000000000000).toString(16), `8000000000000`);
+shouldBe((-0x8000000000000).toString(16), `-8000000000000`);
+shouldBe((0x8000000000000 - 1).toString(16), `7ffffffffffff`);
+shouldBe((-0x8000000000000 + 1).toString(16), `-7ffffffffffff`);
index b0aa25a..13d6bc7 100644 (file)
@@ -1,3 +1,30 @@
+2017-01-24  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Optimize Number#toString with Int52
+        https://bugs.webkit.org/show_bug.cgi?id=167303
+
+        Reviewed by Sam Weinig.
+
+        In kraken crypto-sha256-iterative, we frequently call Number.prototype.toString with
+        Int52. In that case, toString handles it in the generic double path. But we should
+        have a fast path for this since it can be represented in int64_t.
+
+        The stanford-crypto-sha256-iterative shows 1.6% performance improvement (on Linux machine hanayamata).
+
+            Collected 100 samples per benchmark/VM, with 100 VM invocations per benchmark. Emitted a call to gc() between
+            sample measurements. Used 1 benchmark iteration per VM invocation for warm-up. Used the jsc-specific preciseTime()
+            function to get microsecond-level timing. Reporting benchmark execution times with 95% confidence intervals in
+            milliseconds.
+
+                                                       baseline                  patched
+
+            stanford-crypto-sha256-iterative        32.853+-0.075      ^      32.325+-0.055         ^ definitely 1.0163x faster
+
+        * runtime/JSCJSValue.h:
+        * runtime/NumberPrototype.cpp:
+        (JSC::int52ToStringWithRadix):
+        (JSC::toStringWithRadix):
+
 2017-01-24  Michael Saboff  <msaboff@apple.com>
 
         InferredTypeTable entry manipulation is not TOCTOU race safe
index 8809fdc..a6254de 100644 (file)
@@ -316,9 +316,9 @@ public:
 
     // Constants used for Int52. Int52 isn't part of JSValue right now, but JSValues may be
     // converted to Int52s and back again.
-    static const unsigned numberOfInt52Bits = 52;
-    static const int64_t notInt52 = static_cast<int64_t>(1) << numberOfInt52Bits;
-    static const unsigned int52ShiftAmount = 12;
+    static constexpr const unsigned numberOfInt52Bits = 52;
+    static constexpr const int64_t notInt52 = static_cast<int64_t>(1) << numberOfInt52Bits;
+    static constexpr const unsigned int52ShiftAmount = 12;
     
     static ptrdiff_t offsetOfPayload() { return OBJECT_OFFSETOF(JSValue, u.asBits.payload); }
     static ptrdiff_t offsetOfTag() { return OBJECT_OFFSETOF(JSValue, u.asBits.tag); }
index dc96ee4..bf32bac 100644 (file)
@@ -144,9 +144,30 @@ typedef char RadixBuffer[2180];
 // Mapping from integers 0..35 to digit identifying this value, for radix 2..36.
 static const char radixDigits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
 
-static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radix)
+static char* int52ToStringWithRadix(char* startOfResultString, int64_t int52Value, unsigned radix)
 {
-    ASSERT(std::isfinite(number));
+    bool negative = false;
+    uint64_t positiveNumber = int52Value;
+    if (int52Value < 0) {
+        negative = true;
+        positiveNumber = -int52Value;
+    }
+
+    do {
+        uint64_t index = positiveNumber % radix;
+        ASSERT(index < sizeof(radixDigits));
+        *--startOfResultString = radixDigits[index];
+        positiveNumber /= radix;
+    } while (positiveNumber);
+    if (negative)
+        *--startOfResultString = '-';
+
+    return startOfResultString;
+}
+
+static char* toStringWithRadix(RadixBuffer& buffer, double originalNumber, unsigned radix)
+{
+    ASSERT(std::isfinite(originalNumber));
     ASSERT(radix >= 2 && radix <= 36);
 
     // Position the decimal point at the center of the string, set
@@ -155,32 +176,38 @@ static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radi
     char* startOfResultString = decimalPoint;
 
     // Extract the sign.
-    bool isNegative = number < 0;
-    if (std::signbit(number))
-        number = -number;
+    bool isNegative = originalNumber < 0;
+    double number = originalNumber;
+    if (std::signbit(originalNumber))
+        number = -originalNumber;
     double integerPart = floor(number);
 
-    // We use this to test for odd values in odd radix bases.
-    // Where the base is even, (e.g. 10), to determine whether a value is even we need only
-    // consider the least significant digit. For example, 124 in base 10 is even, because '4'
-    // is even. if the radix is odd, then the radix raised to an integer power is also odd.
-    // E.g. in base 5, 124 represents (1 * 125 + 2 * 25 + 4 * 5). Since each digit in the value
-    // is multiplied by an odd number, the result is even if the sum of all digits is even.
-    //
-    // For the integer portion of the result, we only need test whether the integer value is
-    // even or odd. For each digit of the fraction added, we should invert our idea of whether
-    // the number is odd if the new digit is odd.
-    //
-    // Also initialize digit to this value; for even radix values we only need track whether
-    // the last individual digit was odd.
-    bool integerPartIsOdd = integerPart <= static_cast<double>(0x1FFFFFFFFFFFFFull) && static_cast<int64_t>(integerPart) & 1;
-    ASSERT(integerPartIsOdd == static_cast<bool>(fmod(integerPart, 2)));
-    bool isOddInOddRadix = integerPartIsOdd;
-    uint32_t digit = integerPartIsOdd;
-
     // Check if the value has a fractional part to convert.
     double fractionPart = number - integerPart;
-    if (fractionPart) {
+    if (!fractionPart) {
+        *decimalPoint = '\0';
+        // We do not need to care the negative zero (-0) since it is also converted to "0" in all the radix.
+        if (integerPart < (static_cast<int64_t>(1) << (JSValue::numberOfInt52Bits - 1)))
+            return int52ToStringWithRadix(startOfResultString, static_cast<int64_t>(originalNumber), radix);
+    } else {
+        // We use this to test for odd values in odd radix bases.
+        // Where the base is even, (e.g. 10), to determine whether a value is even we need only
+        // consider the least significant digit. For example, 124 in base 10 is even, because '4'
+        // is even. if the radix is odd, then the radix raised to an integer power is also odd.
+        // E.g. in base 5, 124 represents (1 * 125 + 2 * 25 + 4 * 5). Since each digit in the value
+        // is multiplied by an odd number, the result is even if the sum of all digits is even.
+        //
+        // For the integer portion of the result, we only need test whether the integer value is
+        // even or odd. For each digit of the fraction added, we should invert our idea of whether
+        // the number is odd if the new digit is odd.
+        //
+        // Also initialize digit to this value; for even radix values we only need track whether
+        // the last individual digit was odd.
+        bool integerPartIsOdd = integerPart <= static_cast<double>(0x1FFFFFFFFFFFFFull) && static_cast<int64_t>(integerPart) & 1;
+        ASSERT(integerPartIsOdd == static_cast<bool>(fmod(integerPart, 2)));
+        bool isOddInOddRadix = integerPartIsOdd;
+        uint32_t digit = integerPartIsOdd;
+
         // Write the decimal point now.
         *decimalPoint = '.';
 
@@ -310,8 +337,7 @@ static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radi
 
         *endOfResultString = '\0';
         ASSERT(endOfResultString < buffer + sizeof(buffer));
-    } else
-        *decimalPoint = '\0';
+    }
 
     BigInteger units(integerPart);
 
@@ -321,7 +347,7 @@ static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radi
 
         // Read a single digit and write it to the front of the string.
         // Divide by radix to remove one digit from the value.
-        digit = units.divide(radix);
+        uint32_t digit = units.divide(radix);
         *--startOfResultString = radixDigits[digit];
     } while (!!units);