From a71f2e60ff8507b08ebd180dfe8484cc47b4ecd4 Mon Sep 17 00:00:00 2001 From: miloyip Date: Fri, 5 Sep 2014 19:51:20 +0800 Subject: [PATCH] Optimize ParseNumber() --- include/rapidjson/reader.h | 45 ++++++++---- test/perftest/rapidjsontest.cpp | 9 +++ test/unittest/readertest.cpp | 118 ++++++++++++++++---------------- 3 files changed, 98 insertions(+), 74 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 4aa98b18..0816b1f1 100644 --- a/include/rapidjson/reader.h +++ b/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(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(s.Take() - '0'); + i64 = i64 * 10 + static_cast(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; } diff --git a/test/perftest/rapidjsontest.cpp b/test/perftest/rapidjsontest.cpp index 6a451131..2321947c 100644 --- a/test/perftest/rapidjsontest.cpp +++ b/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(s, h)); + } +} + TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterative_DummyHandler)) { for (size_t i = 0; i < kTrialCount; i++) { StringStream s(json_); diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index a82aacfa..bbd33d97 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -184,7 +184,7 @@ static void TestParseDouble() { StringStream s(str); \ ParseDoubleHandler h; \ Reader reader; \ - reader.Parse(s, h); \ + ASSERT_EQ(kParseErrorNone, reader.Parse(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 + 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); -{ - 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); -} - -// 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 buffer[32]; - *internal::dtoa(u.d, buffer) = '\0'; - TEST_DOUBLE(fullPrecision, buffer, u.d); + { + 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); } -} + +#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 }