[JSC] Optimize number parsing and string parsing in LiteralParser
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Apr 2016 00:45:27 +0000 (00:45 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Apr 2016 00:45:27 +0000 (00:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156896

Reviewed by Mark Lam.

This patch aim to improve JSON.parse performance. Major 2 optimizations are included.

1. Change `double result` to `int32_t result` in integer parsing case.
We already have the optimized path for integer parsing, when it's digits are less than 10.
At that case, the maximum number is 999999999, and the minimum number is -99999999.
The both are in range of Int32. So We can use int32_t for accumulation instead of double.

2. Add the string parsing fast / slow cases.
We add the fast case for string parsing, which does not include any escape sequences.

Both optimizations improve Kraken json-parse-financial, roughly 3.5 - 4.5%.

json-parse-financial        49.128+-1.589             46.979+-0.912           might be 1.0457x faster

* runtime/LiteralParser.cpp:
(JSC::isJSONWhiteSpace):
(JSC::isSafeStringCharacter):
(JSC::LiteralParser<CharType>::Lexer::lexString):
(JSC::LiteralParser<CharType>::Lexer::lexStringSlow):
(JSC::LiteralParser<CharType>::Lexer::lexNumber):
* runtime/LiteralParser.h:

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/LiteralParser.cpp
Source/JavaScriptCore/runtime/LiteralParser.h

index 3708fd4..6910ad9 100644 (file)
@@ -1,3 +1,32 @@
+2016-04-22  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Optimize number parsing and string parsing in LiteralParser
+        https://bugs.webkit.org/show_bug.cgi?id=156896
+
+        Reviewed by Mark Lam.
+
+        This patch aim to improve JSON.parse performance. Major 2 optimizations are included.
+
+        1. Change `double result` to `int32_t result` in integer parsing case.
+        We already have the optimized path for integer parsing, when it's digits are less than 10.
+        At that case, the maximum number is 999999999, and the minimum number is -99999999.
+        The both are in range of Int32. So We can use int32_t for accumulation instead of double.
+
+        2. Add the string parsing fast / slow cases.
+        We add the fast case for string parsing, which does not include any escape sequences.
+
+        Both optimizations improve Kraken json-parse-financial, roughly 3.5 - 4.5%.
+
+        json-parse-financial        49.128+-1.589             46.979+-0.912           might be 1.0457x faster
+
+        * runtime/LiteralParser.cpp:
+        (JSC::isJSONWhiteSpace):
+        (JSC::isSafeStringCharacter):
+        (JSC::LiteralParser<CharType>::Lexer::lexString):
+        (JSC::LiteralParser<CharType>::Lexer::lexStringSlow):
+        (JSC::LiteralParser<CharType>::Lexer::lexNumber):
+        * runtime/LiteralParser.h:
+
 2016-04-22  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Source directives lost when using Function constructor repeatedly
index f554983..40eef7c 100644 (file)
@@ -43,7 +43,7 @@
 namespace JSC {
 
 template <typename CharType>
-static inline bool isJSONWhiteSpace(const CharType& c)
+static ALWAYS_INLINE bool isJSONWhiteSpace(const CharType& c)
 {
     // The JSON RFC 4627 defines a list of allowed characters to be considered
     // insignificant white space: http://www.ietf.org/rfc/rfc4627.txt (2. JSON Grammar).
@@ -336,12 +336,12 @@ ALWAYS_INLINE void setParserTokenString<UChar>(LiteralParserToken<UChar>& token,
     token.stringToken16 = string;
 }
 
-template <ParserMode mode, typename CharType, LChar terminator> static inline bool isSafeStringCharacter(LChar c)
+template <ParserMode mode, typename CharType, LChar terminator> static ALWAYS_INLINE bool isSafeStringCharacter(LChar c)
 {
     return (c >= ' ' && c != '\\' && c != terminator) || (c == '\t' && mode != StrictJSON);
 }
 
-template <ParserMode mode, typename CharType, UChar terminator> static inline bool isSafeStringCharacter(UChar c)
+template <ParserMode mode, typename CharType, UChar terminator> static ALWAYS_INLINE bool isSafeStringCharacter(UChar c)
 {
     return (c >= ' ' && (mode == StrictJSON || c <= 0xff) && c != '\\' && c != terminator) || (c == '\t' && mode != StrictJSON);
 }
@@ -351,13 +351,32 @@ template <ParserMode mode, char terminator> ALWAYS_INLINE TokenType LiteralParse
 {
     ++m_ptr;
     const CharType* runStart = m_ptr;
+    while (m_ptr < m_end && isSafeStringCharacter<mode, CharType, terminator>(*m_ptr))
+        ++m_ptr;
+    if (LIKELY(m_ptr < m_end && *m_ptr == terminator)) {
+        token.stringBuffer = String();
+        setParserTokenString<CharType>(token, runStart);
+        token.stringLength = m_ptr - runStart;
+        token.type = TokString;
+        token.end = ++m_ptr;
+        return TokString;
+    }
+    return lexStringSlow<mode, terminator>(token, runStart);
+}
+
+template <typename CharType>
+template <ParserMode mode, char terminator> TokenType LiteralParser<CharType>::Lexer::lexStringSlow(LiteralParserToken<CharType>& token, const CharType* runStart)
+{
     StringBuilder builder;
+    goto slowPathBegin;
     do {
         runStart = m_ptr;
         while (m_ptr < m_end && isSafeStringCharacter<mode, CharType, terminator>(*m_ptr))
             ++m_ptr;
-        if (builder.length())
+        if (!builder.isEmpty())
             builder.append(runStart, m_ptr - runStart);
+
+slowPathBegin:
         if ((mode != NonStrictJSON) && m_ptr < m_end && *m_ptr == '\\') {
             if (builder.isEmpty() && runStart < m_ptr)
                 builder.append(runStart, m_ptr - runStart);
@@ -486,6 +505,7 @@ TokenType LiteralParser<CharType>::Lexer::lexNumber(LiteralParserToken<CharType>
     }
 
     // ('.' [0-9]+)?
+    const int NumberOfDigitsForSafeInt32 = 9;  // The numbers from -99999999 to 999999999 are always in range of Int32.
     if (m_ptr < m_end && *m_ptr == '.') {
         ++m_ptr;
         // [0-9]+
@@ -497,21 +517,29 @@ TokenType LiteralParser<CharType>::Lexer::lexNumber(LiteralParserToken<CharType>
         ++m_ptr;
         while (m_ptr < m_end && isASCIIDigit(*m_ptr))
             ++m_ptr;
-    } else if (m_ptr < m_end && (*m_ptr != 'e' && *m_ptr != 'E') && (m_ptr - token.start) < 10) {
-        double result = 0;
+    } else if (m_ptr < m_end && (*m_ptr != 'e' && *m_ptr != 'E') && (m_ptr - token.start) <= NumberOfDigitsForSafeInt32) {
+        int32_t result = 0;
         token.type = TokNumber;
         token.end = m_ptr;
         const CharType* digit = token.start;
-        int negative = 1;
+        bool negative = false;
         if (*digit == '-') {
-            negative = -1;
+            negative = true;
             digit++;
         }
         
+        ASSERT((m_ptr - digit) <= NumberOfDigitsForSafeInt32);
         while (digit < m_ptr)
             result = result * 10 + (*digit++) - '0';
-        result *= negative;
-        token.numberToken = result;
+
+        if (!negative)
+            token.numberToken = result;
+        else {
+            if (!result)
+                token.numberToken = -0.0;
+            else
+                token.numberToken = -result;
+        }
         return TokNumber;
     }
 
index fcb79fa..9e22f38 100644 (file)
@@ -141,6 +141,7 @@ private:
         template <ParserMode mode> TokenType lex(LiteralParserToken<CharType>&);
         ALWAYS_INLINE TokenType lexIdentifier(LiteralParserToken<CharType>&);
         template <ParserMode mode, char terminator> ALWAYS_INLINE TokenType lexString(LiteralParserToken<CharType>&);
+        template <ParserMode mode, char terminator> TokenType lexStringSlow(LiteralParserToken<CharType>&, const CharType* runStart);
         ALWAYS_INLINE TokenType lexNumber(LiteralParserToken<CharType>&);
         LiteralParserToken<CharType> m_currentToken;
         ParserMode m_mode;