- strToInt benchmark and more optimization

- warnmsg macro in CppUnit
This commit is contained in:
Aleksandar Fabijanic 2012-10-01 00:52:53 +00:00
parent bf74a4be8b
commit 8a4eafd05a
5 changed files with 145 additions and 119 deletions

View File

@ -1,7 +1,7 @@
This is the changelog file for the POCO C++ Libraries. This is the changelog file for the POCO C++ Libraries.
Release 1.5.0 (2012-09-??) Release 1.5.0 (2012-10-01)
========================== ==========================
- added JSON - added JSON
@ -43,6 +43,8 @@ Release 1.5.0 (2012-09-??)
- fixed SF#307 Detect the SQL driver type at run time - fixed SF#307 Detect the SQL driver type at run time
- added VS 2012 Projects/Solutions - added VS 2012 Projects/Solutions
- enhanced and accelerated numeric parsing for integers and floats - enhanced and accelerated numeric parsing for integers and floats
- fixed SF#590 Segfault on FreeBSD when stack size not rounded
- added warn function and warnmsg macro in CppUnit
Release 1.4.4p1 (2012-??-??) Release 1.4.4p1 (2012-??-??)
============================ ============================

View File

@ -162,9 +162,9 @@ protected:
long lineNumber = CppUnitException::CPPUNIT_UNKNOWNLINENUMBER, long lineNumber = CppUnitException::CPPUNIT_UNKNOWNLINENUMBER,
const std::string& fileName = CppUnitException::CPPUNIT_UNKNOWNFILENAME); const std::string& fileName = CppUnitException::CPPUNIT_UNKNOWNFILENAME);
void warn(const std::string& message = "", void warn(const std::string& message = "",
long lineNumber = CppUnitException::CPPUNIT_UNKNOWNLINENUMBER, long lineNumber = CppUnitException::CPPUNIT_UNKNOWNLINENUMBER,
const std::string& fileName = CppUnitException::CPPUNIT_UNKNOWNFILENAME); const std::string& fileName = CppUnitException::CPPUNIT_UNKNOWNFILENAME);
private: private:
@ -247,6 +247,9 @@ inline std::string TestCase::toString()
#define failmsg(msg) \ #define failmsg(msg) \
(this->fail(msg, __LINE__, __FILE__)) (this->fail(msg, __LINE__, __FILE__))
#define warnmsg(msg) \
(this->fail(msg, __LINE__, __FILE__))
} // namespace CppUnit } // namespace CppUnit

View File

@ -57,138 +57,106 @@ namespace Poco {
inline char decimalSeparator() inline char decimalSeparator()
/// Returns decimal separator from global locale or /// Returns decimal separator from global locale or
/// default '.' for platforms where locale is unavailable. /// default '.' for platforms where locale is unavailable.
{ {
#if !defined(POCO_NO_LOCALE) #if !defined(POCO_NO_LOCALE)
return std::use_facet<std::numpunct<char> >(std::locale()).decimal_point(); return std::use_facet<std::numpunct<char> >(std::locale()).decimal_point();
#else #else
return '.'; return '.';
#endif #endif
} }
inline char thousandSeparator() inline char thousandSeparator()
/// Returns thousand separator from global locale or /// Returns thousand separator from global locale or
/// default ',' for platforms where locale is unavailable. /// default ',' for platforms where locale is unavailable.
{ {
#if !defined(POCO_NO_LOCALE) #if !defined(POCO_NO_LOCALE)
return std::use_facet<std::numpunct<char> >(std::locale()).thousands_sep(); return std::use_facet<std::numpunct<char> >(std::locale()).thousands_sep();
#else #else
return ','; return ',';
#endif #endif
} }
template <typename I> template <typename I>
bool strToInt(const char* pStr, I& result, short base = -1) bool strToInt(const char* pStr, I& result, short base)
/// Converts zero-terminated array to integer number; /// Converts zero-terminated character array to integer number;
/// If base is equal to -1, this functin will try to determine /// Thousand separators are recognized for base10 and current locale;
/// the numeric base (using '0' prefix for octal, '0x' for /// it is silently skipped but not verified for correct positioning.
/// hexadecimal and no prefix for decimal). /// Function returns true if succesful. If parsing was unsuccesful,
/// Thousand separators are recognized for base10 and the locale /// the return value is false with the result value undetermined.
/// and silently skipped but not verified for correct positioning.
/// Returns true if succesful. If parsing was unsuccesful,
/// the return value is false with result value undetermined.
{ {
if (!std::numeric_limits<I>::is_integer) return false; if (!pStr) return false;
while (*pStr == ' ') ++pStr;
if (!pStr || (pStr && *pStr == '\0')) return false;
while ((*pStr != '\0') && (*pStr == ' ')) ++pStr;
if (*pStr == '\0') return false; if (*pStr == '\0') return false;
I sign = 1;
char sign = 1; if ((base == 10) && (*pStr == '-'))
if (*pStr == '-')
{ {
++pStr;
sign = -1; sign = -1;
++pStr;
} }
else if (*pStr == '+') ++pStr; else if (*pStr == '+') ++pStr;
if (*pStr == '\0') return false;
result = 0;
if (*pStr == '0')
{
while ((*pStr != '\0') && (*pStr == '0')) ++pStr;
if (*pStr == '\0')
{
result = 0;
return true;
}
if ((*pStr == 'x') || (*pStr == 'X'))
{
base = 0x10;
++pStr;
if (*pStr == '\0') return false;
}
else if (base == -1) base = 010;
}
else if (base == -1) base = 10;
while ((*pStr == '0'))
{
if (*pStr != '\0')
{
result = 0;
return true;
}
++pStr;
}
if (*pStr == '\0') return false;
// parser states:
const char STATE_SIGNIFICANT_DIGITS = 1;
char state = 0;
const char thSep = thousandSeparator(); const char thSep = thousandSeparator();
bool allowDigits = true;
result = 0;
I limitCheck = std::numeric_limits<I>::max() / base;
for (; *pStr != '\0'; ++pStr) for (; *pStr != '\0'; ++pStr)
{ {
switch (*pStr) switch (*pStr)
{ {
case '0': case '1': case '2': case '3': case '4': case 'x': case 'X':
if (base != 0x10) return false;
case '0':
if (state < STATE_SIGNIFICANT_DIGITS) break;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '5': case '6': case '7':
if (allowDigits) if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
{ if (result > limitCheck) return false;
if (result > (std::numeric_limits<I>::max() / base)) return false; result = result * base + (*pStr - '0');
result = result * base + (*pStr - '0');
}
else return false;
break; break;
case '8': case '9': case '8': case '9':
if (allowDigits && (base == 10 || base == 16)) if ((base == 10) || (base == 0x10))
{ {
if (result > (std::numeric_limits<I>::max() / base)) return false; if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
if (result > limitCheck) return false;
result = result * base + (*pStr - '0'); result = result * base + (*pStr - '0');
} }
else return false; else return false;
break; break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
if (allowDigits && base == 16) if (base != 0x10) return false;
{ if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
if (result > (std::numeric_limits<I>::max() / base)) return false; if (result > limitCheck) return false;
result = result * base + (10 + *pStr - 'a'); result = result * base + (10 + *pStr - 'a');
}
else return false;
break; break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
if (allowDigits && base == 16) if (base != 0x10) return false;
{
if (result > (std::numeric_limits<I>::max() / base)) return false; if (state < STATE_SIGNIFICANT_DIGITS) state = STATE_SIGNIFICANT_DIGITS;
result = result * base + (10 + *pStr - 'A'); if (result > limitCheck) return false;
} result = result * base + (10 + *pStr - 'A');
else
return false;
break; break;
case 'U': case 'U':
case 'u': case 'u':
case 'L': case 'L':
case 'l': case 'l':
allowDigits = false; goto done;
break;
case '.': case '.':
if ((base == 10) && (thSep == '.')) break; if ((base == 10) && (thSep == '.')) break;
@ -199,23 +167,23 @@ bool strToInt(const char* pStr, I& result, short base = -1)
else return false; else return false;
case ' ': case ' ':
if (base == 10) break; if ((base == 10) && (thSep == ' ')) break;
else return false; goto done;
default: default:
return false; return false;
} }
} }
if ((base == 10) && (std::numeric_limits<I>::is_signed)) done:
result *= sign; if ((sign < 0) && (base == 10)) result *= sign;
return true; return true;
} }
template <typename I> template <typename I>
bool strToInt(const std::string& str, I& result, short base = -1) bool strToInt(const std::string& str, I& result, short base)
/// Converts string to integer number; /// Converts string to integer number;
/// This is a wrapper function, for details see see the /// This is a wrapper function, for details see see the
/// bool strToInt(const char*, I&, short&) implementation. /// bool strToInt(const char*, I&, short&) implementation.

View File

@ -564,24 +564,63 @@ void StringTest::testNumericLocale()
} catch (std::runtime_error& ex) } catch (std::runtime_error& ex)
{ {
std::cout << ex.what() << std::endl; std::cout << ex.what() << std::endl;
warn ("Locale not found, skipping test"); warnmsg ("Locale not found, skipping test");
} }
#endif #endif
} }
bool parseStream(const std::string& s, double& value) void StringTest::benchmarkStrToInt()
{ {
MemoryInputStream istr(s.data(), s.size()); Poco::Stopwatch sw;
#if !defined(POCO_NO_LOCALE) int number = 123456789;
istr.imbue(std::locale::classic()); std::string num = "123456789";
#endif int res;
istr >> value; sw.start();
return istr.eof() && !istr.fail(); for (int i = 0; i < 1000000; ++i) parseStream(num, res);
sw.stop();
std::cout << "parseStream Number: " << res << std::endl;
double timeStream = sw.elapsed() / 1000.0;
char* pC = 0;
sw.restart();
for (int i = 0; i < 1000000; ++i) res = std::strtol(num.c_str(), &pC, 10);
sw.stop();
std::cout << "std::strtol Number: " << res << std::endl;
double timeStrtol = sw.elapsed() / 1000.0;
sw.restart();
for (int i = 0; i < 1000000; ++i) strToInt(num.c_str(), res, 10);
sw.stop();
std::cout << "strToInt Number: " << res << std::endl;
double timeStrToInt = sw.elapsed() / 1000.0;
sw.restart();
for (int i = 0; i < 1000000; ++i) std::sscanf(num.c_str(), "%d%c", &res);
sw.stop();
std::cout << "sscanf Number: " << res << std::endl;
double timeScanf = sw.elapsed() / 1000.0;
int graph;
std::cout << std::endl << "Timing and speedup relative to I/O stream:" << std::endl << std::endl;
std::cout << std::setw(14) << "Stream:\t" << std::setw(10) << std::setfill(' ') << timeStream << "[ms]" << std::endl;
std::cout << std::setw(14) << "std::strtol:\t" << std::setw(10) << std::setfill(' ') << timeStrtol << "[ms]" <<
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrtol) << '\t' ;
graph = (int) (timeStream / timeStrtol); for (int i = 0; i < graph; ++i) std::cout << '|';
std::cout << std::endl << std::setw(14) << "strToInt:\t" << std::setw(10) << std::setfill(' ') << timeStrToInt << "[ms]" <<
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeStrToInt) << '\t' ;
graph = (int) (timeStream / timeStrToInt); for (int i = 0; i < graph; ++i) std::cout << '|';
std::cout << std::endl << std::setw(14) << "std::sscanf:\t" << std::setw(10) << std::setfill(' ') << timeScanf << "[ms]" <<
std::setw(10) << std::setfill(' ') << "Speedup: " << (timeStream / timeScanf) << '\t' ;
graph = (int) (timeStream / timeScanf); for (int i = 0; i < graph; ++i) std::cout << '|';
std::cout << std::endl;
} }
void StringTest::benchmark() void StringTest::benchmarkStrToFloat()
{ {
Poco::Stopwatch sw; Poco::Stopwatch sw;
double number = 1.23456e-123; double number = 1.23456e-123;
@ -599,7 +638,7 @@ void StringTest::benchmark()
sw.stop(); sw.stop();
std::cout << "std::strtod Number: " << res << std::endl; std::cout << "std::strtod Number: " << res << std::endl;
double timeStrtod = sw.elapsed() / 1000.0; double timeStrtod = sw.elapsed() / 1000.0;
sw.restart(); sw.restart();
char ou = 0; char ou = 0;
for (int i = 0; i < 1000000; ++i) strToFloat(num.c_str(), res, ou); for (int i = 0; i < 1000000; ++i) strToFloat(num.c_str(), res, ou);
@ -662,7 +701,8 @@ CppUnit::Test* StringTest::suite()
CppUnit_addTest(pSuite, StringTest, testStringToFloat); CppUnit_addTest(pSuite, StringTest, testStringToFloat);
CppUnit_addTest(pSuite, StringTest, testStringToFloatError); CppUnit_addTest(pSuite, StringTest, testStringToFloatError);
CppUnit_addTest(pSuite, StringTest, testNumericLocale); CppUnit_addTest(pSuite, StringTest, testNumericLocale);
CppUnit_addTest(pSuite, StringTest, benchmark); //CppUnit_addTest(pSuite, StringTest, benchmarkStrToFloat);
//CppUnit_addTest(pSuite, StringTest, benchmarkStrToInt);
return pSuite; return pSuite;
} }

View File

@ -66,7 +66,8 @@ public:
void testStringToFloat(); void testStringToFloat();
void testStringToFloatError(); void testStringToFloatError();
void testNumericLocale(); void testNumericLocale();
void benchmark(); void benchmarkStrToFloat();
void benchmarkStrToInt();
void setUp(); void setUp();
void tearDown(); void tearDown();
@ -80,43 +81,55 @@ private:
{ {
T result = 0; T result = 0;
if (123 <= std::numeric_limits<T>::max()) if (123 <= std::numeric_limits<T>::max())
assert(strToInt("123", result)); assert(result == 123); assert(strToInt("123", result, 10)); assert(result == 123);
assert(strToInt("0", result)); assert(result == 0); assert(strToInt("0", result, 10)); assert(result == 0);
assert(strToInt("000", result)); assert(result == 0); assert(strToInt("000", result, 10)); assert(result == 0);
if (123 < std::numeric_limits<T>::max()) if (123 < std::numeric_limits<T>::max())
{ assert(strToInt(" 123 ", result)); assert(result == 123); } { assert(strToInt(" 123 ", result, 10)); assert(result == 123); }
if (123 < std::numeric_limits<T>::max()) if (123 < std::numeric_limits<T>::max())
{ assert(strToInt(" 123", result)); assert(result == 123); } { assert(strToInt(" 123", result, 10)); assert(result == 123); }
if (123 < std::numeric_limits<T>::max()) if (123 < std::numeric_limits<T>::max())
{ assert(strToInt("123 ", result)); assert(result == 123); } { assert(strToInt("123 ", result, 10)); assert(result == 123); }
if (std::numeric_limits<T>::is_signed && (-123 > std::numeric_limits<T>::min())) if (std::numeric_limits<T>::is_signed && (-123 > std::numeric_limits<T>::min()))
{ assert(strToInt("-123", result)); assert(result == -123); } { assert(strToInt("-123", result, 10)); assert(result == -123); }
if (0x123 < std::numeric_limits<T>::max()) if (0x123 < std::numeric_limits<T>::max())
{ assert(strToInt("123", result, 0x10)); assert(result == 0x123); } { assert(strToInt("123", result, 0x10)); assert(result == 0x123); }
if (0x12ab < std::numeric_limits<T>::max()) if (0x12ab < std::numeric_limits<T>::max())
{ assert(strToInt("12AB", result, 0x10)); assert(result == 0x12ab); } { assert(strToInt("12AB", result, 0x10)); assert(result == 0x12ab); }
if (0x12ab < std::numeric_limits<T>::max()) if (0x12ab < std::numeric_limits<T>::max())
{ assert(strToInt("0X12AB", result)); assert(result == 0x12ab); } { assert(strToInt("0X12AB", result, 0x10)); assert(result == 0x12ab); }
if (0x12ab < std::numeric_limits<T>::max()) if (0x12ab < std::numeric_limits<T>::max())
{ assert(strToInt("0x12AB", result)); assert(result == 0x12ab); } { assert(strToInt("0x12AB", result, 0x10)); assert(result == 0x12ab); }
if (0x12ab < std::numeric_limits<T>::max()) if (0x12ab < std::numeric_limits<T>::max())
{ assert(strToInt("0x12aB", result)); assert(result == 0x12ab); } { assert(strToInt("0x12aB", result, 0x10)); assert(result == 0x12ab); }
if (0x98fe < std::numeric_limits<T>::max()) if (0x98fe < std::numeric_limits<T>::max())
{ assert(strToInt("0X98Fe", result)); assert(result == 0x98fe); } { assert(strToInt("0X98Fe", result, 0x10)); assert(result == 0x98fe); }
if (123 < std::numeric_limits<T>::max()) if (123 < std::numeric_limits<T>::max())
{ assert(strToInt("0x0", result)); assert(result == 0); } { assert(strToInt("0x0", result, 0x10)); assert(result == 0); }
if (123 < std::numeric_limits<T>::max()) if (123 < std::numeric_limits<T>::max())
{ assert(strToInt("00", result, 0x10)); assert(result == 0); } { assert(strToInt("00", result, 0x10)); assert(result == 0); }
if (0123 < std::numeric_limits<T>::max()) if (0123 < std::numeric_limits<T>::max())
{ assert(strToInt("123", result, 010)); assert(result == 0123); } { assert(strToInt("123", result, 010)); assert(result == 0123); }
if (0123 < std::numeric_limits<T>::max()) if (0123 < std::numeric_limits<T>::max())
{ assert(strToInt("0123", result)); assert(result == 0123); } { assert(strToInt("0123", result, 010)); assert(result == 0123); }
assert(strToInt("0", result, 010)); assert(result == 0); assert(strToInt("0", result, 010)); assert(result == 0);
assert(strToInt("000", result)); assert(result == 0); assert(strToInt("000", result, 010)); assert(result == 0);
} }
template <typename T>
bool parseStream(const std::string& s, T& value)
{
MemoryInputStream istr(s.data(), s.size());
#if !defined(POCO_NO_LOCALE)
istr.imbue(std::locale::classic());
#endif
istr >> value;
return istr.eof() && !istr.fail();
}
}; };