Browse Source

Fixed a performance regression parsing doubles

tags/2021-05-28
tpoole 7 years ago
parent
commit
3627603c83
2 changed files with 138 additions and 27 deletions
  1. +0
    -5
      modules/juce_core/system/juce_StandardHeader.h
  2. +138
    -22
      modules/juce_core/text/juce_CharacterFunctions.h

+ 0
- 5
modules/juce_core/system/juce_StandardHeader.h View File

@@ -114,11 +114,6 @@
#include "../misc/juce_StdFunctionCompat.h" #include "../misc/juce_StdFunctionCompat.h"
#endif #endif
// The live build fails to compile std::stringstream
#if ! JUCE_PROJUCER_LIVE_BUILD
#include <sstream>
#endif
// Include std::atomic if it's supported by the compiler // Include std::atomic if it's supported by the compiler
#if JUCE_ATOMIC_AVAILABLE #if JUCE_ATOMIC_AVAILABLE
#include <atomic> #include <atomic>


+ 138
- 22
modules/juce_core/text/juce_CharacterFunctions.h View File

@@ -61,7 +61,7 @@ namespace juce
#endif #endif
//============================================================================== //==============================================================================
/** GNU libstdc++ does not have std::make_unsigned */
// GNU libstdc++ does not have std::make_unsigned
namespace internal namespace internal
{ {
template <typename Type> struct make_unsigned { typedef Type type; }; template <typename Type> struct make_unsigned { typedef Type type; };
@@ -140,20 +140,29 @@ public:
template <typename CharPointerType> template <typename CharPointerType>
static double readDoubleValue (CharPointerType& text) noexcept static double readDoubleValue (CharPointerType& text) noexcept
{ {
const int maxSignificantDigits = 17 + 1; // An additional digit for rounding
const int bufferSize = maxSignificantDigits + 7 + 1; // -.E-XXX and a trailing null-terminator
#if JUCE_MINGW
bool isNegative = false;
#else
JUCE_CONSTEXPR const int maxSignificantDigits = 17 + 1; // An additional digit for rounding
JUCE_CONSTEXPR const int bufferSize = maxSignificantDigits + 7 + 1; // -.E-XXX and a trailing null-terminator
char buffer[bufferSize] = {}; char buffer[bufferSize] = {};
char* currentCharacter = &(buffer[0]); char* currentCharacter = &(buffer[0]);
int numSigFigs = 0;
bool decimalPointFound = false;
#endif
text = text.findEndOfWhitespace(); text = text.findEndOfWhitespace();
auto c = *text; auto c = *text;
switch (c) switch (c)
{ {
case '-': *currentCharacter++ = '-'; // Fall-through..
case '+': c = *++text;
case '-':
#if JUCE_MINGW
isNegative = true;
#else
*currentCharacter++ = '-';
#endif
// Fall-through..
case '+':
c = *++text;
} }
switch (c) switch (c)
@@ -171,11 +180,121 @@ public:
break; break;
} }
#if JUCE_MINGW
// MinGW does not have access to the locale functions required for strtold, so we parse the doubles
// ourselves. There are some edge cases where the least significant digit will be wrong!
double result[3] = { 0 }, accumulator[2] = { 0 };
int exponentAdjustment[2] = { 0 }, exponentAccumulator[2] = { -1, -1 };
int exponent = 0, decPointIndex = 0, digit = 0;
int lastDigit = 0, numSignificantDigits = 0;
bool digitsFound = false;
JUCE_CONSTEXPR const int maxSignificantDigits = 15 + 2;
for (;;)
{
if (text.isDigit())
{
lastDigit = digit;
digit = (int) text.getAndAdvance() - '0';
digitsFound = true;
if (decPointIndex != 0)
exponentAdjustment[1]++;
if (numSignificantDigits == 0 && digit == 0)
continue;
if (++numSignificantDigits > maxSignificantDigits)
{
if (digit > 5)
++accumulator [decPointIndex];
else if (digit == 5 && (lastDigit & 1) != 0)
++accumulator [decPointIndex];
if (decPointIndex > 0)
exponentAdjustment[1]--;
else
exponentAdjustment[0]++;
while (text.isDigit())
{
++text;
if (decPointIndex == 0)
exponentAdjustment[0]++;
}
}
else
{
const auto maxAccumulatorValue = (double) ((std::numeric_limits<unsigned int>::max() - 9) / 10);
if (accumulator [decPointIndex] > maxAccumulatorValue)
{
result [decPointIndex] = mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex])
+ accumulator [decPointIndex];
accumulator [decPointIndex] = 0;
exponentAccumulator [decPointIndex] = 0;
}
accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit;
exponentAccumulator [decPointIndex]++;
}
}
else if (decPointIndex == 0 && *text == '.')
{
++text;
decPointIndex = 1;
if (numSignificantDigits > maxSignificantDigits)
{
while (text.isDigit())
++text;
break;
}
}
else
{
break;
}
}
result[0] = mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0];
if (decPointIndex != 0)
result[1] = mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1];
c = *text;
if ((c == 'e' || c == 'E') && digitsFound)
{
auto negativeExponent = false;
switch (*++text)
{
case '-': negativeExponent = true; // fall-through..
case '+': ++text;
}
while (text.isDigit())
exponent = (exponent * 10) + ((int) text.getAndAdvance() - '0');
if (negativeExponent)
exponent = -exponent;
}
auto r = mulexp10 (result[0], exponent + exponentAdjustment[0]);
if (decPointIndex != 0)
r += mulexp10 (result[1], exponent - exponentAdjustment[1]);
return isNegative ? -r : r;
#else // ! JUCE_MINGW
int numSigFigs = 0;
bool decimalPointFound = false;
for (;;) for (;;)
{ {
if (text.isDigit()) if (text.isDigit())
{ {
int digit = (int) text.getAndAdvance() - '0';
auto digit = (int) text.getAndAdvance() - '0';
if (numSigFigs >= maxSignificantDigits if (numSigFigs >= maxSignificantDigits
|| ((numSigFigs == 0 && (! decimalPointFound)) && digit == 0)) || ((numSigFigs == 0 && (! decimalPointFound)) && digit == 0))
@@ -231,22 +350,19 @@ public:
*currentCharacter++ = '0'; *currentCharacter++ = '0';
} }
#if JUCE_PROJUCER_LIVE_BUILD
// This will change with locale!
return strtod (&buffer[0], nullptr);
#if JUCE_WINDOWS
static _locale_t locale = _create_locale (LC_ALL, "C");
return _strtod_l (&buffer[0], nullptr, locale);
#else #else
double result = 0;
const size_t stringSize = (size_t) (currentCharacter - &buffer[0]) + 1;
if (stringSize > 1)
{
std::istringstream is (std::string (&buffer[0], stringSize));
is.imbue (std::locale ("C"));
is >> result;
}
return result;
static locale_t locale = newlocale (LC_ALL_MASK, "C", nullptr);
#if JUCE_ANDROID
return (double) strtold_l (&buffer[0], nullptr, locale);
#else
return strtod_l (&buffer[0], nullptr, locale);
#endif
#endif #endif
#endif // JUCE_MINGW
} }
/** Parses a character string, to read a floating-point value. */ /** Parses a character string, to read a floating-point value. */


Loading…
Cancel
Save