Parcourir la source

Optimize ParseNumber()

miloyip il y a 11 ans
Parent
commit
a71f2e60ff
3 fichiers modifiés avec 97 ajouts et 73 suppressions
  1. 31 14
      include/rapidjson/reader.h
  2. 9 0
      test/perftest/rapidjsontest.cpp
  3. 57 59
      test/unittest/readertest.cpp

+ 31 - 14
include/rapidjson/reader.h

@@ -730,8 +730,8 @@ private:
         NumberStream(GenericReader& reader, InputStream& is) : is(is) { (void)reader;  }
         ~NumberStream() {}
 
-        Ch Peek() { return is.Peek(); }
-        Ch Take() { return is.Take(); }
+        RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); }
+        RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); }
         size_t Tell() { return is.Tell(); }
         const char* Pop() { return 0; }
 
@@ -748,7 +748,7 @@ private:
         NumberStream(GenericReader& reader, InputStream& is) : NumberStream<InputStream, false>(reader, is), stackStream(reader.stack_) {}
         ~NumberStream() {}
 
-        Ch Take() {
+        RAPIDJSON_FORCEINLINE Ch Take() {
             stackStream.Put((char)Base::is.Peek());
             return Base::is.Take();
         }
@@ -869,28 +869,45 @@ private:
             s.Take();
 
             if (!useDouble) {
-                d = use64bit ? i64 : i;
-                useDouble = true;
-
+#if RAPIDJSON_64BIT
+                // Use i64 to store significand in 64-bit architecture
+                if (!use64bit)
+                    i64 = i;
+        
                 while (s.Peek() >= '0' && s.Peek() <= '9') {
-                    if (d >= 9007199254740991.0) {
-                        if (parseFlags & kParseFullPrecisionFlag)
+                    if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) { // 2^53 - 1 for fast path
+                        if (parseFlags & kParseFullPrecisionFlag) {
+                            while (s.Peek() >= '0' && s.Peek() <= '9')
+                                s.Take();
                             useStrtod = true;
+                            --expFrac;
+                        }
                         break;
                     }
                     else {
-                        d = d * 10.0 + static_cast<unsigned>(s.Take() - '0');
+                        i64 = i64 * 10 + static_cast<unsigned>(s.Take() - '0');
                         --expFrac;
                     }
                 }
+
+                if (!useStrtod)
+                    d = (double)i64;
+#else
+                // Use double to store significand in 32-bit architecture
+                d = use64bit ? (double)i64 : (double)i;
+#endif
+                useDouble = true;
             }
 
-            while (s.Peek() >= '0' && s.Peek() <= '9') {
-                //s.Take();
-                if (parseFlags & kParseFullPrecisionFlag)
+            if ((parseFlags & kParseFullPrecisionFlag) == 0 || !useStrtod) {
+                while (s.Peek() >= '0' && s.Peek() <= '9') {
+                    d = d * 10.0 + (s.Take() - '0');
+                    --expFrac;
+                }
+            }
+            else {
+                while (s.Peek() >= '0' && s.Peek() <= '9')
                     s.Take();
-                else
-                    d = d * 10 + (s.Take() - '0');
                 --expFrac;
             }
 

+ 9 - 0
test/perftest/rapidjsontest.cpp

@@ -96,6 +96,15 @@ TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler)) {
     }
 }
 
+TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_FullPrecision)) {
+    for (size_t i = 0; i < kTrialCount; i++) {
+        StringStream s(json_);
+        BaseReaderHandler<> h;
+        Reader reader;
+        EXPECT_TRUE(reader.Parse<kParseFullPrecisionFlag>(s, h));
+    }
+}
+
 TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterative_DummyHandler)) {
     for (size_t i = 0; i < kTrialCount; i++) {
         StringStream s(json_);

+ 57 - 59
test/unittest/readertest.cpp

@@ -184,7 +184,7 @@ static void TestParseDouble() {
         StringStream s(str); \
         ParseDoubleHandler h; \
         Reader reader; \
-        reader.Parse<fullPrecision ? kParseFullPrecisionFlag : 0>(s, h); \
+        ASSERT_EQ(kParseErrorNone, reader.Parse<fullPrecision ? kParseFullPrecisionFlag : 0>(s, h).Code()); \
         EXPECT_EQ(1u, h.step_); \
         if (fullPrecision) { \
             EXPECT_EQ(x, h.actual_); \
@@ -195,67 +195,65 @@ static void TestParseDouble() {
             EXPECT_DOUBLE_EQ(x, h.actual_); \
     }
 
-TEST_DOUBLE(fullPrecision, "0.0", 0.0);
-TEST_DOUBLE(fullPrecision, "1.0", 1.0);
-TEST_DOUBLE(fullPrecision, "-1.0", -1.0);
-TEST_DOUBLE(fullPrecision, "1.5", 1.5);
-TEST_DOUBLE(fullPrecision, "-1.5", -1.5);
-TEST_DOUBLE(fullPrecision, "3.1416", 3.1416);
-TEST_DOUBLE(fullPrecision, "1E10", 1E10);
-TEST_DOUBLE(fullPrecision, "1e10", 1e10);
-TEST_DOUBLE(fullPrecision, "1E+10", 1E+10);
-TEST_DOUBLE(fullPrecision, "1E-10", 1E-10);
-TEST_DOUBLE(fullPrecision, "-1E10", -1E10);
-TEST_DOUBLE(fullPrecision, "-1e10", -1e10);
-TEST_DOUBLE(fullPrecision, "-1E+10", -1E+10);
-TEST_DOUBLE(fullPrecision, "-1E-10", -1E-10);
-TEST_DOUBLE(fullPrecision, "1.234E+10", 1.234E+10);
-TEST_DOUBLE(fullPrecision, "1.234E-10", 1.234E-10);
-TEST_DOUBLE(fullPrecision, "1.79769e+308", 1.79769e+308);
-TEST_DOUBLE(fullPrecision, "2.22507e-308", 2.22507e-308);
-TEST_DOUBLE(fullPrecision, "-1.79769e+308", -1.79769e+308);
-TEST_DOUBLE(fullPrecision, "-2.22507e-308", -2.22507e-308);
-TEST_DOUBLE(fullPrecision, "4.9406564584124654e-324", 4.9406564584124654e-324); // minimum denormal
-TEST_DOUBLE(fullPrecision, "1e-10000", 0.0);                                   // must underflow
-TEST_DOUBLE(fullPrecision, "18446744073709551616", 18446744073709551616.0);    // 2^64 (max of uint64_t + 1, force to use double)
-TEST_DOUBLE(fullPrecision, "-9223372036854775809", -9223372036854775809.0);    // -2^63 - 1(min of int64_t + 1, force to use double)
-TEST_DOUBLE(fullPrecision, "0.9868011474609375", 0.9868011474609375);          // https://github.com/miloyip/rapidjson/issues/120
-TEST_DOUBLE(fullPrecision, "123e34", 123e34);                                  // Fast Path Cases In Disguise
-
-{
-    char n1e308[310];   // '1' followed by 308 '0'
-    n1e308[0] = '1';
-    for (int i = 1; i < 309; i++)
-        n1e308[i] = '0';
-    n1e308[309] = '\0';
-    TEST_DOUBLE(fullPrecision, n1e308, 1E308);
-}
+    TEST_DOUBLE(fullPrecision, "0.0", 0.0);
+    TEST_DOUBLE(fullPrecision, "1.0", 1.0);
+    TEST_DOUBLE(fullPrecision, "-1.0", -1.0);
+    TEST_DOUBLE(fullPrecision, "1.5", 1.5);
+    TEST_DOUBLE(fullPrecision, "-1.5", -1.5);
+    TEST_DOUBLE(fullPrecision, "3.1416", 3.1416);
+    TEST_DOUBLE(fullPrecision, "1E10", 1E10);
+    TEST_DOUBLE(fullPrecision, "1e10", 1e10);
+    TEST_DOUBLE(fullPrecision, "1E+10", 1E+10);
+    TEST_DOUBLE(fullPrecision, "1E-10", 1E-10);
+    TEST_DOUBLE(fullPrecision, "-1E10", -1E10);
+    TEST_DOUBLE(fullPrecision, "-1e10", -1e10);
+    TEST_DOUBLE(fullPrecision, "-1E+10", -1E+10);
+    TEST_DOUBLE(fullPrecision, "-1E-10", -1E-10);
+    TEST_DOUBLE(fullPrecision, "1.234E+10", 1.234E+10);
+    TEST_DOUBLE(fullPrecision, "1.234E-10", 1.234E-10);
+    TEST_DOUBLE(fullPrecision, "1.79769e+308", 1.79769e+308);
+    TEST_DOUBLE(fullPrecision, "2.22507e-308", 2.22507e-308);
+    TEST_DOUBLE(fullPrecision, "-1.79769e+308", -1.79769e+308);
+    TEST_DOUBLE(fullPrecision, "-2.22507e-308", -2.22507e-308);
+    TEST_DOUBLE(fullPrecision, "4.9406564584124654e-324", 4.9406564584124654e-324); // minimum denormal
+    TEST_DOUBLE(fullPrecision, "1e-10000", 0.0);                                   // must underflow
+    TEST_DOUBLE(fullPrecision, "18446744073709551616", 18446744073709551616.0);    // 2^64 (max of uint64_t + 1, force to use double)
+    TEST_DOUBLE(fullPrecision, "-9223372036854775809", -9223372036854775809.0);    // -2^63 - 1(min of int64_t + 1, force to use double)
+    TEST_DOUBLE(fullPrecision, "0.9868011474609375", 0.9868011474609375);          // https://github.com/miloyip/rapidjson/issues/120
+    TEST_DOUBLE(fullPrecision, "123e34", 123e34);                                  // Fast Path Cases In Disguise
+    TEST_DOUBLE(fullPrecision, "45913141877270640000.0", 45913141877270640000.0);
 
-// Random test for double
-{
-    union {
-        double d;
-        uint64_t u;
-    }u;
-    Random r;
-
-    for (unsigned i = 0; i < 100000; i++) {
-        do {
-            // Need to call r() in two statements for cross-platform coherent sequence.
-            u.u = uint64_t(r()) << 32;
-            u.u |= uint64_t(r());
-        } while (std::isnan(u.d) || std::isinf(u.d)
-#ifdef _MSC_VER
-            // VC's strtod() has problem with denormal numbers
-            || !std::isnormal(u.d)
-#endif
-            );
+    {
+        char n1e308[310];   // '1' followed by 308 '0'
+        n1e308[0] = '1';
+        for (int i = 1; i < 309; i++)
+            n1e308[i] = '0';
+        n1e308[309] = '\0';
+        TEST_DOUBLE(fullPrecision, n1e308, 1E308);
+    }
 
-        char buffer[32];
-        *internal::dtoa(u.d, buffer) = '\0';
-        TEST_DOUBLE(fullPrecision, buffer, u.d);
+#if 1
+    // Random test for double
+    {
+        union {
+            double d;
+            uint64_t u;
+        }u;
+        Random r;
+
+        for (unsigned i = 0; i < 100000; i++) {
+            do {
+                // Need to call r() in two statements for cross-platform coherent sequence.
+                u.u = uint64_t(r()) << 32;
+                u.u |= uint64_t(r());
+            } while (std::isnan(u.d) || std::isinf(u.d) || !std::isnormal(u.d));
+
+            char buffer[32];
+            *internal::dtoa(u.d, buffer) = '\0';
+            TEST_DOUBLE(fullPrecision, buffer, u.d);
+        }
     }
-}
+#endif
 
 #undef TEST_DOUBLE
 }