- 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.
Release 1.5.0 (2012-09-??)
Release 1.5.0 (2012-10-01)
==========================
- added JSON
@ -43,6 +43,8 @@ Release 1.5.0 (2012-09-??)
- fixed SF#307 Detect the SQL driver type at run time
- added VS 2012 Projects/Solutions
- 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-??-??)
============================

View File

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

View File

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

View File

@ -564,24 +564,63 @@ void StringTest::testNumericLocale()
} catch (std::runtime_error& ex)
{
std::cout << ex.what() << std::endl;
warn ("Locale not found, skipping test");
warnmsg ("Locale not found, skipping test");
}
#endif
}
bool parseStream(const std::string& s, double& value)
void StringTest::benchmarkStrToInt()
{
MemoryInputStream istr(s.data(), s.size());
#if !defined(POCO_NO_LOCALE)
istr.imbue(std::locale::classic());
#endif
istr >> value;
return istr.eof() && !istr.fail();
Poco::Stopwatch sw;
int number = 123456789;
std::string num = "123456789";
int res;
sw.start();
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;
double number = 1.23456e-123;
@ -599,7 +638,7 @@ void StringTest::benchmark()
sw.stop();
std::cout << "std::strtod Number: " << res << std::endl;
double timeStrtod = sw.elapsed() / 1000.0;
sw.restart();
char ou = 0;
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, testStringToFloatError);
CppUnit_addTest(pSuite, StringTest, testNumericLocale);
CppUnit_addTest(pSuite, StringTest, benchmark);
//CppUnit_addTest(pSuite, StringTest, benchmarkStrToFloat);
//CppUnit_addTest(pSuite, StringTest, benchmarkStrToInt);
return pSuite;
}

View File

@ -66,7 +66,8 @@ public:
void testStringToFloat();
void testStringToFloatError();
void testNumericLocale();
void benchmark();
void benchmarkStrToFloat();
void benchmarkStrToInt();
void setUp();
void tearDown();
@ -80,43 +81,55 @@ private:
{
T result = 0;
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("000", result)); assert(result == 0);
assert(strToInt("0", result, 10)); assert(result == 0);
assert(strToInt("000", result, 10)); assert(result == 0);
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())
{ assert(strToInt(" 123", result)); assert(result == 123); }
{ assert(strToInt(" 123", result, 10)); assert(result == 123); }
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()))
{ assert(strToInt("-123", result)); assert(result == -123); }
{ assert(strToInt("-123", result, 10)); assert(result == -123); }
if (0x123 < std::numeric_limits<T>::max())
{ assert(strToInt("123", result, 0x10)); assert(result == 0x123); }
if (0x12ab < std::numeric_limits<T>::max())
{ assert(strToInt("12AB", result, 0x10)); assert(result == 0x12ab); }
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())
{ assert(strToInt("0x12AB", result)); assert(result == 0x12ab); }
{ assert(strToInt("0x12AB", result, 0x10)); assert(result == 0x12ab); }
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())
{ assert(strToInt("0X98Fe", result)); assert(result == 0x98fe); }
{ assert(strToInt("0X98Fe", result, 0x10)); assert(result == 0x98fe); }
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())
{ assert(strToInt("00", result, 0x10)); assert(result == 0); }
if (0123 < std::numeric_limits<T>::max())
{ assert(strToInt("123", result, 010)); assert(result == 0123); }
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("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();
}
};