Thread: [Quickfix-developers] FieldConvertor.h optimizations
Brought to you by:
orenmnero
From: Peterson, K. <kri...@rb...> - 2004-08-12 19:13:59
|
I have optimized some of the field convertors in FieldConvertors.h and have= achieved substantial improvements in performance (generally 50% - 200% fas= ter). I include a patch against the FieldConvertors.h file from quickfix-1.= 7.1. The code is admittedly 'C-ish', however no interface has been changed and I= believe the results speak for themselves. Replacing strtod() in the double to string conversion would probably be a s= ignificant win, although I don't know of any efficient algorithms for this = conversion. Finally, time stamp conversions should probably be added to pt as well. Cheers, - Kris The times were obtained from pt running on an AthlonXP 2500 running Linux k= ernel 2.6.7 compiled with g++ 3.3.4. 'normal' builds used the existing Fiel= dConvertors.h, 'modified' builds used the modified FieldConvertors.h. Optimization flags for the builds were as follows: quickfix-debug-modified/Makefile:CFLAGS =3D -g -Wall quickfix-debug-normal/Makefile:CFLAGS =3D -g -Wall quickfix-optimized-modified/Makefile:CFLAGS =3D -g -O3 -ffast-math -mcpu=3D= athlon-xp -Wall quickfix-optimized-normal/Makefile:CFLAGS =3D -g -O3 -ffast-math -mcpu=3Dat= hlon-xp -Wall times-debug-normal.txt : peterson@karl src $ ./pt -c 100000 times-debug-modified.txt : peterson@karl src $ ./pt -c 100000 times-optimized-normal.txt : peterson@karl src $ ./pt -c 100000 times-optimized-modified.txt: peterson@karl src $ ./pt -c 100000 : Converting integers to strings: times-debug-normal.txt : num: 100000, seconds: 0.259, num_per_seco= nd: 386100 times-debug-modified.txt : num: 100000, seconds: 0.032, num_per_seco= nd: 3.125e+06 times-optimized-normal.txt : num: 100000, seconds: 0.183, num_per_seco= nd: 546448 times-optimized-modified.txt: num: 100000, seconds: 0.026, num_per_seco= nd: 3.84615e+06 : Converting strings to integers: times-debug-normal.txt : num: 100000, seconds: 0.024, num_per_seco= nd: 4.16667e+06 times-debug-modified.txt : num: 100000, seconds: 0.008, num_per_seco= nd: 1.25e+07 times-optimized-normal.txt : num: 100000, seconds: 0.016, num_per_seco= nd: 6.25e+06 times-optimized-modified.txt: num: 100000, seconds: 0.004, num_per_seco= nd: 2.5e+07 : Converting doubles to strings: times-debug-normal.txt : num: 100000, seconds: 0.711, num_per_seco= nd: 140647 times-debug-modified.txt : num: 100000, seconds: 0.243, num_per_seco= nd: 411523 times-optimized-normal.txt : num: 100000, seconds: 0.531, num_per_seco= nd: 188324 times-optimized-modified.txt: num: 100000, seconds: 0.236, num_per_seco= nd: 423729 : Converting strings to doubles: times-debug-normal.txt : num: 100000, seconds: 0.987, num_per_seco= nd: 101317 times-debug-modified.txt : num: 100000, seconds: 0.058, num_per_seco= nd: 1.72414e+06 times-optimized-normal.txt : num: 100000, seconds: 0.725, num_per_seco= nd: 137931 times-optimized-modified.txt: num: 100000, seconds: 0.062, num_per_seco= nd: 1.6129e+06 : Creating Heartbeat messages: times-debug-normal.txt : num: 100000, seconds: 1.823, num_per_seco= nd: 54854.6 times-debug-modified.txt : num: 100000, seconds: 1.166, num_per_seco= nd: 85763.3 times-optimized-normal.txt : num: 100000, seconds: 1.242, num_per_seco= nd: 80515.3 times-optimized-modified.txt: num: 100000, seconds: 0.825, num_per_seco= nd: 121212 : Serializing Heartbeat messages to strings: times-debug-normal.txt : num: 100000, seconds: 2.35, num_per_secon= d: 42553.2 times-debug-modified.txt : num: 100000, seconds: 1.198, num_per_seco= nd: 83472.5 times-optimized-normal.txt : num: 100000, seconds: 1.425, num_per_seco= nd: 70175.4 times-optimized-modified.txt: num: 100000, seconds: 0.707, num_per_seco= nd: 141443 : Serializing Heartbeat messages from strings: times-debug-normal.txt : num: 100000, seconds: 4.749, num_per_seco= nd: 21057.1 times-debug-modified.txt : num: 100000, seconds: 2.671, num_per_seco= nd: 37439.2 times-optimized-normal.txt : num: 100000, seconds: 3.053, num_per_seco= nd: 32754.7 times-optimized-modified.txt: num: 100000, seconds: 1.782, num_per_seco= nd: 56116.7 : Creating NewOrderSingle messages: times-debug-normal.txt : num: 100000, seconds: 6.422, num_per_seco= nd: 15571.5 times-debug-modified.txt : num: 100000, seconds: 3.937, num_per_seco= nd: 25400.1 times-optimized-normal.txt : num: 100000, seconds: 4.778, num_per_seco= nd: 20929.3 times-optimized-modified.txt: num: 100000, seconds: 3.026, num_per_seco= nd: 33046.9 : Serializing NewOrderSingle messages to string= s: times-debug-normal.txt : num: 100000, seconds: 6.429, num_per_seco= nd: 15554.5 times-debug-modified.txt : num: 100000, seconds: 3.84, num_per_secon= d: 26041.7 times-optimized-normal.txt : num: 100000, seconds: 4.874, num_per_seco= nd: 20517 times-optimized-modified.txt: num: 100000, seconds: 3.029, num_per_seco= nd: 33014.2 : Serializing NewOrderSingle messages from stri= ngs: times-debug-normal.txt : num: 100000, seconds: 9.608, num_per_seco= nd: 10408 times-debug-modified.txt : num: 100000, seconds: 5.385, num_per_seco= nd: 18570.1 times-optimized-normal.txt : num: 100000, seconds: 7.202, num_per_seco= nd: 13885 times-optimized-modified.txt: num: 100000, seconds: 3.779, num_per_seco= nd: 26462 : Creating QuoteRequest messages: times-debug-normal.txt : num: 100000, seconds: 93.295, num_per_sec= ond: 1071.87 times-debug-modified.txt : num: 100000, seconds: 46.975, num_per_sec= ond: 2128.79 times-optimized-normal.txt : num: 100000, seconds: 61.148, num_per_sec= ond: 1635.38 times-optimized-modified.txt: num: 100000, seconds: 33.904, num_per_sec= ond: 2949.5 : Serializing QuoteRequest messages to strings: times-debug-normal.txt : num: 100000, seconds: 11.052, num_per_sec= ond: 9048.14 times-debug-modified.txt : num: 100000, seconds: 10.691, num_per_sec= ond: 9353.66 times-optimized-normal.txt : num: 100000, seconds: 5.667, num_per_seco= nd: 17646 times-optimized-modified.txt: num: 100000, seconds: 4.808, num_per_seco= nd: 20798.7 : Serializing QuoteRequest messages from string= s: times-debug-normal.txt : num: 100000, seconds: 74.91, num_per_seco= nd: 1334.94 times-debug-modified.txt : num: 100000, seconds: 48.556, num_per_sec= ond: 2059.48 times-optimized-normal.txt : num: 100000, seconds: 47.944, num_per_sec= ond: 2085.77 times-optimized-modified.txt: num: 100000, seconds: 28.627, num_per_sec= ond: 3493.21 : Reading fields from QuoteRequest message: times-debug-normal.txt : num: 100000, seconds: 34.526, num_per_sec= ond: 2896.37 times-debug-modified.txt : num: 100000, seconds: 12.988, num_per_sec= ond: 7699.41 times-optimized-normal.txt : num: 100000, seconds: 21.891, num_per_sec= ond: 4568.09 times-optimized-modified.txt: num: 100000, seconds: 6.875, num_per_seco= nd: 14545.5 : Storing NewOrderSingle messages: times-debug-normal.txt : num: 100000, seconds: 0.765, num_per_seco= nd: 130719 times-debug-modified.txt : num: 100000, seconds: 0.71, num_per_secon= d: 140845 times-optimized-normal.txt : num: 100000, seconds: 0.575, num_per_seco= nd: 173913 times-optimized-modified.txt: num: 100000, seconds: 0.521, num_per_seco= nd: 191939 : Validating NewOrderSingle messages with no da= ta dictionary: times-debug-normal.txt : num: 100000, seconds: 1.489, num_per_seco= nd: 67159.2 times-debug-modified.txt : num: 100000, seconds: 0.859, num_per_seco= nd: 116414 times-optimized-normal.txt : num: 100000, seconds: 0.843, num_per_seco= nd: 118624 times-optimized-modified.txt: num: 100000, seconds: 0.426, num_per_seco= nd: 234742 : Validating NewOrderSingle messages with data = dictionary: times-debug-normal.txt : num: 100000, seconds: 7.732, num_per_seco= nd: 12933.3 times-debug-modified.txt : num: 100000, seconds: 6.105, num_per_seco= nd: 16380 times-optimized-normal.txt : num: 100000, seconds: 2.485, num_per_seco= nd: 40241.4 times-optimized-modified.txt: num: 100000, seconds: 1.717, num_per_seco= nd: 58241.1 --- quickfix/src/C++/FieldConvertors.h 2004-04-29 20:58:20.000000000 +0100 +++ quickfix-debug-modified/src/C++/FieldConvertors.h 2004-08-12 19:13:22= .036829060 +0100 @@ -1,3 +1,4 @@ + /* -*- C++ -*- */ /*************************************************************************= *** @@ -32,6 +33,76 @@ namespace FIX { +template<class T> +static inline char * integer_to_string (char * buf, const size_t len, T t) +{ + const bool is_neg =3D t < 0; + char * p =3D buf + len; + + *--p =3D '\0'; + + if (is_neg) { + if (t =3D=3D std::numeric_limits<T>::min()) { + *--p =3D '0' + (10-t%10)%10; + t/=3D10; + } + t=3D-t; + do { + *--p =3D '0' + t % 10; + t /=3D 10; + } while (t > 0); + *--p =3D '-'; + } else { + do { + *--p =3D '0' + t % 10; + t /=3D 10; + } while (t > 0); + } + return p; +} + + + +template<class T> +static inline char * integer_to_string_padded (char * buf, + const size_t len, + T t, + const size_t width =3D 0, + const char padding_char =3D '0') +{ + if (!width) return integer_to_string (buf, len, t); + + const bool is_neg =3D t < 0; + char * p =3D buf + len; + + *--p =3D '\0'; + + if (is_neg) { + if (t =3D=3D std::numeric_limits<T>::min()) { + *--p =3D '0' + (10-t%10)%10; + t/=3D10; + } + t=3D-t; + do { + *--p =3D '0' + t % 10; + t /=3D 10; + } while (t > 0); + if (p > buf) + *--p =3D '-'; + } else { + do { + *--p =3D '0' + t % 10; + t /=3D 10; + } while (t > 0); + } + const char * stop_p =3D buf + len - width - 1; + if (stop_p < buf) stop_p =3D buf; + while (p > stop_p) + *--p =3D padding_char; + return p; +} + + /// Empty convertor is a no-op. struct EmptyConvertor { @@ -46,21 +117,32 @@ { static std::string convert( long value ) { - char temp[ 12 ]; - memset( temp, 0, 12 * sizeof( char ) ); - sprintf( temp, "%d", ( int ) value ); - return temp; + // buffer is big enough for significant digits and extra digit, minus = and null + char buffer [std::numeric_limits<long>::digits10 + 3]; + const char * const start =3D integer_to_string (buffer, sizeof (buffer= ), value); + return std::string (start, buffer + sizeof (buffer) - start - 1); } static bool convert( const std::string& value, long& result ) { const char * str =3D value.c_str(); - if ( *str =3D=3D '-' ) ++str; - for ( const char * p =3D str; *p !=3D 0; ++p ) - { - if ( !isdigit( *p ) ) return false; + bool is_neg =3D false; + long x =3D 0; + + if (*str =3D=3D '-') { + is_neg =3D true; + ++str; } - result =3D atol( value.c_str() ); + + do { + const int c =3D *str - '0'; + if (c < 0 || 9 < c) return false; + x =3D 10 * x + c; + } while (*++str); + + if (is_neg) x =3D -x; + + result =3D x; return true; } @@ -81,9 +163,10 @@ throw( FieldConvertError& ) { if ( value > 255 || value < 0 ) throw FieldConvertError(); - std::stringstream stream; - stream << std::setw( 3 ) << std::setfill( '0' ) << value; - return stream.str(); + char result [4]; + if (integer_to_string_padded (result, sizeof(result), value, 3, '0') != =3D result) + throw FieldConvertError(); + return std::string (result, 3); } static bool convert( const std::string& value, long& result ) @@ -104,53 +187,32 @@ static std::string convert( double value ) { char result[ 32 ]; - memset( result, 0, 32 * sizeof( char ) ); - sprintf( result, "%.15g", value ); - return result; + return std::string (result, std::sprintf( result, "%.15g", value )); } static bool convert( const std::string& value, double& result ) { - if ( !value.size() ) return false; - if ( value =3D=3D "." ) return false; - - result =3D atof( const_cast < char* > ( value.c_str() ) ); - std::string stripped =3D value; - - // add leading zero if none exists - if ( *stripped.begin() =3D=3D '.' ) - stripped =3D '0' + stripped; - - // remove extra leading zeros - while ( stripped.size() > 1 - && *(stripped.begin()) =3D=3D '0' - && isdigit(*(stripped.begin()+1)) ) - { - stripped.erase( stripped.begin() ); + const char * i =3D value.c_str(); + + if (!*i) return false; // Catch null strin= gs + if (*i =3D=3D '-' && !*++i) return false; // Eat leading = '-' and recheck for null string + + bool have_digit =3D false; + + if (std::isdigit (*i)) { + have_digit =3D true; + while (std::isdigit (*++i)); } - if ( stripped.find( '.' ) !=3D std::string::npos ) - { - std::string::reverse_iterator r =3D stripped.rbegin(); - while ( r !=3D stripped.rend() && ( *r =3D=3D '0' || *r =3D=3D '.' )= ) - { - if ( *r =3D=3D '.' ) { stripped.resize( stripped.size() - 1 ); bre= ak; } - stripped.resize( stripped.size() - 1 ); r++; - } - } - if ( *stripped.begin() =3D=3D '0' ) - { - std::string::size_type i =3D stripped.find_first_not_of( '0' ); - std::string::size_type dot =3D stripped.find( '.' ); - if ( i !=3D dot ) - stripped =3D stripped.substr( i ); + if (*i =3D=3D '.' && std::isdigit (*++i)) { + have_digit =3D true; + while (std::isdigit (*++i)); } - std::string converted =3D convert( result ); - if ( stripped.size() !=3D converted.size() ) return false; + if (*i || !have_digit) return false; - // strcmp is being used because =3D=3D operator is funky under linux - return ( strcmp( stripped.c_str(), converted.c_str() ) =3D=3D 0 ); + result =3D std::strtod (value.c_str(), 0); + return true; } static double convert( const std::string& value ) @@ -169,16 +231,13 @@ { static std::string convert( char value ) { - char temp[ 2 ]; - temp[ 0 ] =3D value; - temp[ 1 ] =3D '\0'; - return temp; + return std::string (1, value); } static bool convert( const std::string& value, char& result ) { - if ( value.size() =3D=3D 0 ) return false; - result =3D *value.c_str(); + if ( value.size() !=3D 1 ) return false; + result =3D value[0]; return true; } @@ -197,15 +256,19 @@ struct BoolConvertor { static std::string convert( bool value ) - { return value ? "Y" : "N"; } + { + const char ch =3D value ? 'Y' : 'N'; + return std::string (1, ch); + } static bool convert( const std::string& value, bool& result ) { - if ( value =3D=3D "Y" ) result =3D true; - else - if ( value =3D=3D "N" ) result =3D false; - else - return false; + if (value.size() !=3D 1) return false; + switch (value[0]) { + case 'Y': result =3D true; break; + case 'N': result =3D false; break; + default: return false; + } return true; } @@ -227,13 +290,19 @@ throw( FieldConvertError& ) { char result[ 18+4 ]; - int len =3D strftime( result, 18, "%Y%m%d-%H:%M:%S", value ); - if ( len !=3D 17 ) throw FieldConvertError(); + + integer_to_string_padded (result , 5, static_cast<const tm*>(value= )->tm_year + 1900, 4, '0'); + integer_to_string_padded (result + 4, 3, static_cast<const tm*>(value= )->tm_mon + 1, 2, '0'); + integer_to_string_padded (result + 6, 3, static_cast<const tm*>(value= )->tm_mday, 2, '0'); result[8] =3D '-'; + integer_to_string_padded (result + 9, 3, static_cast<const tm*>(value= )->tm_hour, 2, '0'); result[11] =3D ':'; + integer_to_string_padded (result + 12, 3, static_cast<const tm*>(value= )->tm_min, 2, '0'); result[14] =3D ':'; + integer_to_string_padded (result + 15, 3, static_cast<const tm*>(value= )->tm_sec, 2, '0'); if(true=3D=3DshowMilliseconds) { - len =3D sprintf(result+17,".%03d",value.getMillisecond()); - if ( len !=3D 4) throw FieldConvertError(); + result[17] =3D '.'; + if (integer_to_string_padded (result + 18, 4, value.getMillisecond()= , 3, '0') !=3D result + 18) + throw FieldConvertError(); } return result; } @@ -242,19 +311,73 @@ throw( FieldConvertError& ) { UtcTimeStamp result; - const char* val =3D value.c_str(); - const char* len =3D strptime( val, "%Y%m%d-%H:%M:%S", result ); - if ( len - val !=3D 17 ) throw FieldConvertError(); - - // if we have milliseconds in the string, *len should be ".sss" - result.setMillisecond(0); - if(NULL !=3D len && strlen(len)=3D=3D4 && len[0] =3D=3D '.') - { - int ms =3D atoi(&len[1]); - if(ms < 0 || ms > 999) throw FieldConvertError(); - result.setMillisecond(ms); + bool have_milliseconds =3D false; + + switch (value.size()) { + case 21: have_milliseconds =3D true; + case 17: break; + default: throw FieldConvertError(); } + int i =3D 0; + for (int c=3D0; c<8; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (value[i++] !=3D '-') throw FieldConvertErr= or(); + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (value[i++] !=3D ':') throw FieldConvertErr= or(); + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (value[i++] !=3D ':') throw FieldConvertErr= or(); + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (have_milliseconds) { + if (value[i++] !=3D '.') throw FieldConvertError(); + for (int c=3D0; c<3; ++c) if (!std::isdigit(value[i++])) throw Field= ConvertError(); + } + + tm & result_tm =3D *static_cast<tm*>(result); + + i =3D 0; + + result_tm.tm_year =3D value[i++] - '0'; + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; + result_tm.tm_year -=3D 1900; + + result_tm.tm_mon =3D value[i++] - '0'; + result_tm.tm_mon =3D 10 * result_tm.tm_mon + value[i++] - '0'; + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw FieldConvertE= rror(); + --result_tm.tm_mon; + + result_tm.tm_mday =3D value[i++] - '0'; + result_tm.tm_mday =3D 10 * result_tm.tm_mday + value[i++] - '0'; + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw FieldConver= tError(); + + ++i; // skip '-' + + result_tm.tm_hour =3D value[i++] - '0'; + result_tm.tm_hour =3D 10 * result_tm.tm_hour + value[i++] - '0'; + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No check for= >=3D 0 as no '-' are converted here + + ++i; // skip ':' + + result_tm.tm_min =3D value[i++] - '0'; + result_tm.tm_min =3D 10 * result_tm.tm_min + value[i++] - '0'; + if (59 < result_tm.tm_min) throw FieldConvertError(); // No check for = >=3D 0 as no '-' are converted here + + ++i; // skip ':' + + result_tm.tm_sec =3D value[i++] - '0'; + result_tm.tm_sec =3D 10 * result_tm.tm_sec + value[i++] - '0'; + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No check for = >=3D 0 as no '-' are converted here + + if (have_milliseconds) + result.setMillisecond (100 * (value[i+1] - '0') + + 10 * (value[i+2] - '0') + + (value[i+3] - '0')); + else + result.setMillisecond (0); + + result_tm.tm_isdst =3D -1; + return result; } }; @@ -266,13 +389,16 @@ throw( FieldConvertError& ) { char result[ 9+4 ]; - int len =3D strftime( result, 9, "%H:%M:%S", value ); - if ( len !=3D 8 ) throw FieldConvertError(); + + integer_to_string_padded (result , 3, static_cast<const tm*>(value)= ->tm_hour, 2, '0'); result[2] =3D ':'; + integer_to_string_padded (result + 3, 3, static_cast<const tm*>(value)= ->tm_min, 2, '0'); result[5] =3D ':'; + integer_to_string_padded (result + 6, 3, static_cast<const tm*>(value)= ->tm_sec, 2, '0'); if(true=3D=3DshowMilliseconds) { - len =3D sprintf(result+8,".%03d",value.getMillisecond()); - if ( len !=3D 4) throw FieldConvertError(); + result[8] =3D '.'; + if (integer_to_string_padded (result + 9, 4, value.getMillisecond(),= 3, '0') !=3D result + 9) + throw FieldConvertError(); } return result; @@ -282,19 +408,52 @@ throw( FieldConvertError& ) { UtcTimeOnly result; - const char* val =3D value.c_str(); - const char* len =3D strptime( val, "%H:%M:%S", result ); - if ( len - val !=3D 8 ) throw FieldConvertError(); - - // if we have milliseconds in the string, *len should be ".sss" - result.setMillisecond(0); - if(NULL !=3D len && strlen(len)=3D=3D4 && len[0] =3D=3D '.') - { - int ms =3D atoi(&len[1]); - if(ms < 0 || ms > 999) throw FieldConvertError(); - result.setMillisecond(ms); + bool have_milliseconds =3D false; + + switch (value.size()) { + case 12: have_milliseconds =3D true; + case 8: break; + default: throw FieldConvertError(); + } + + int i =3D 0; + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (value[i++] !=3D ':') throw FieldConvertErr= or(); + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (value[i++] !=3D ':') throw FieldConvertErr= or(); + for (int c=3D0; c<2; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + if (have_milliseconds) { + // ++i instead of i++ skips the '.' separator + for (int c=3D0; c<3; ++c) if (!std::isdigit(value[++i])) throw Field= ConvertError(); } + tm & result_tm =3D *static_cast<tm*>(result); + + i =3D 0; + + result_tm.tm_hour =3D value[i++] - '0'; + result_tm.tm_hour =3D 10 * result_tm.tm_hour + value[i++] - '0'; + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No check for= >=3D 0 as no '-' are converted here + ++i; // skip ':' + + result_tm.tm_min =3D value[i++] - '0'; + result_tm.tm_min =3D 10 * result_tm.tm_min + value[i++] - '0'; + if (59 < result_tm.tm_min) throw FieldConvertError(); // No check for = >=3D 0 as no '-' are converted here + ++i; // skip ':' + + result_tm.tm_sec =3D value[i++] - '0'; + result_tm.tm_sec =3D 10 * result_tm.tm_sec + value[i++] - '0'; + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No check for = >=3D 0 as no '-' are converted here + + if (have_milliseconds) + result.setMillisecond (100 * (value[i+1] - '0') + + 10 * (value[i+2] - '0') + + (value[i+3] - '0')); + else + result.setMillisecond (0); + + result_tm.tm_isdst =3D -1; + return result; } }; @@ -306,8 +465,9 @@ throw( FieldConvertError& ) { char result[ 9 ]; - int len =3D strftime( result, 9, "%Y%m%d", value ); - if ( len !=3D 8 ) throw FieldConvertError(); + integer_to_string_padded (result , 5, static_cast<const tm*>(value= )->tm_year + 1900, 4, '0'); + integer_to_string_padded (result + 4, 3, static_cast<const tm*>(value= )->tm_mon + 1, 2, '0'); + integer_to_string_padded (result + 6, 3, static_cast<const tm*>(value= )->tm_mday, 2, '0'); return result; } @@ -315,9 +475,34 @@ throw( FieldConvertError& ) { UtcDate result; - const char* val =3D value.c_str(); - const char* len =3D strptime( val, "%Y%m%d", result ); - if ( len - val !=3D 8 ) throw FieldConvertError(); + + if (value.size() !=3D 8) throw FieldConvertError(); + + int i =3D 0; + for (int c=3D0; c<8; ++c) if (!std::isdigit(value[i++])) throw FieldCo= nvertError(); + + tm & result_tm =3D *static_cast<tm*>(result); + + i =3D 0; + + result_tm.tm_year =3D value[i++] - '0'; + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; + result_tm.tm_year =3D 10 * result_tm.tm_year + value[i++] - '0'; + result_tm.tm_year -=3D 1900; + + result_tm.tm_mon =3D value[i++] - '0'; + result_tm.tm_mon =3D 10 * result_tm.tm_mon + value[i++] - '0'; + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw FieldConvertE= rror(); + --result_tm.tm_mon; + + result_tm.tm_mday =3D value[i++] - '0'; + result_tm.tm_mday =3D 10 * result_tm.tm_mday + value[i++] - '0'; + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw FieldConver= tError(); + ++i; // skip '-' + + result_tm.tm_isdst =3D -1; + return result; } }; <font face=3D"Times New Roman" size=3D"3"> <p>------------------------------------------------------------------------= ------</p> <p> This email is intended only for the use of the individual(s) to whom it= is addressed and may be privileged and confidential. Unauthorised use or d= isclosure is prohibited. If you receive this e-mail in error, please advise= immediately and delete the original message. This message may have been al= tered without your or our knowledge and the sender does not accept any liab= ility for any errors or omissions in the message.</p> <p>=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D</p> </font> |
From: Oren M. <or...@qu...> - 2004-08-13 15:52:03
|
Thanks Kristopher, I ran these through and was able to see nice performance improvements on serializing to strings, however for some reasons the performance for serializing from strings remained unchanged on the systems I tested under. Also converting from doubles to strings showed no improvement. I tried this under a i686 Xeon and a powerpc chipsets. --oren On Aug 12, 2004, at 2:13 PM, Peterson, Kristofer wrote: > QuickFIX Documentation: > http://www.quickfixengine.org/quickfix/doc/html/index.html > QuickFIX FAQ: > http://www.quickfixengine.org/wikifix/index.php?QuickFixFAQ > QuickFIX Support: http://www.quickfixengine.org/services.html > > I have optimized some of the field convertors in FieldConvertors.h and > have achieved substantial improvements in performance (generally 50% - > 200% faster). I include a patch against the FieldConvertors.h file > from quickfix-1.7.1. > > The code is admittedly 'C-ish', however no interface has been changed > and I believe the results speak for themselves. > > Replacing strtod() in the double to string conversion would probably > be a significant win, although I don't know of any efficient > algorithms for this conversion. > > Finally, time stamp conversions should probably be added to pt as well. > > Cheers, > > - Kris > > The times were obtained from pt running on an AthlonXP 2500 running > Linux kernel 2.6.7 compiled with g++ 3.3.4. 'normal' builds used the > existing FieldConvertors.h, 'modified' builds used the modified > FieldConvertors.h. > > Optimization flags for the builds were as follows: > > quickfix-debug-modified/Makefile:CFLAGS = -g -Wall > quickfix-debug-normal/Makefile:CFLAGS = -g -Wall > quickfix-optimized-modified/Makefile:CFLAGS = -g -O3 -ffast-math > -mcpu=athlon-xp -Wall > quickfix-optimized-normal/Makefile:CFLAGS = -g -O3 -ffast-math > -mcpu=athlon-xp -Wall > > times-debug-normal.txt : peterson@karl src $ ./pt -c 100000 > times-debug-modified.txt : peterson@karl src $ ./pt -c 100000 > times-optimized-normal.txt : peterson@karl src $ ./pt -c 100000 > times-optimized-modified.txt: peterson@karl src $ ./pt -c 100000 > > : Converting integers to strings: > times-debug-normal.txt : num: 100000, seconds: 0.259, > num_per_second: 386100 > times-debug-modified.txt : num: 100000, seconds: 0.032, > num_per_second: 3.125e+06 > times-optimized-normal.txt : num: 100000, seconds: 0.183, > num_per_second: 546448 > times-optimized-modified.txt: num: 100000, seconds: 0.026, > num_per_second: 3.84615e+06 > : Converting strings to integers: > times-debug-normal.txt : num: 100000, seconds: 0.024, > num_per_second: 4.16667e+06 > times-debug-modified.txt : num: 100000, seconds: 0.008, > num_per_second: 1.25e+07 > times-optimized-normal.txt : num: 100000, seconds: 0.016, > num_per_second: 6.25e+06 > times-optimized-modified.txt: num: 100000, seconds: 0.004, > num_per_second: 2.5e+07 > : Converting doubles to strings: > times-debug-normal.txt : num: 100000, seconds: 0.711, > num_per_second: 140647 > times-debug-modified.txt : num: 100000, seconds: 0.243, > num_per_second: 411523 > times-optimized-normal.txt : num: 100000, seconds: 0.531, > num_per_second: 188324 > times-optimized-modified.txt: num: 100000, seconds: 0.236, > num_per_second: 423729 > : Converting strings to doubles: > times-debug-normal.txt : num: 100000, seconds: 0.987, > num_per_second: 101317 > times-debug-modified.txt : num: 100000, seconds: 0.058, > num_per_second: 1.72414e+06 > times-optimized-normal.txt : num: 100000, seconds: 0.725, > num_per_second: 137931 > times-optimized-modified.txt: num: 100000, seconds: 0.062, > num_per_second: 1.6129e+06 > : Creating Heartbeat messages: > times-debug-normal.txt : num: 100000, seconds: 1.823, > num_per_second: 54854.6 > times-debug-modified.txt : num: 100000, seconds: 1.166, > num_per_second: 85763.3 > times-optimized-normal.txt : num: 100000, seconds: 1.242, > num_per_second: 80515.3 > times-optimized-modified.txt: num: 100000, seconds: 0.825, > num_per_second: 121212 > : Serializing Heartbeat messages to > strings: > times-debug-normal.txt : num: 100000, seconds: 2.35, > num_per_second: 42553.2 > times-debug-modified.txt : num: 100000, seconds: 1.198, > num_per_second: 83472.5 > times-optimized-normal.txt : num: 100000, seconds: 1.425, > num_per_second: 70175.4 > times-optimized-modified.txt: num: 100000, seconds: 0.707, > num_per_second: 141443 > : Serializing Heartbeat messages from > strings: > times-debug-normal.txt : num: 100000, seconds: 4.749, > num_per_second: 21057.1 > times-debug-modified.txt : num: 100000, seconds: 2.671, > num_per_second: 37439.2 > times-optimized-normal.txt : num: 100000, seconds: 3.053, > num_per_second: 32754.7 > times-optimized-modified.txt: num: 100000, seconds: 1.782, > num_per_second: 56116.7 > : Creating NewOrderSingle messages: > times-debug-normal.txt : num: 100000, seconds: 6.422, > num_per_second: 15571.5 > times-debug-modified.txt : num: 100000, seconds: 3.937, > num_per_second: 25400.1 > times-optimized-normal.txt : num: 100000, seconds: 4.778, > num_per_second: 20929.3 > times-optimized-modified.txt: num: 100000, seconds: 3.026, > num_per_second: 33046.9 > : Serializing NewOrderSingle messages to > strings: > times-debug-normal.txt : num: 100000, seconds: 6.429, > num_per_second: 15554.5 > times-debug-modified.txt : num: 100000, seconds: 3.84, > num_per_second: 26041.7 > times-optimized-normal.txt : num: 100000, seconds: 4.874, > num_per_second: 20517 > times-optimized-modified.txt: num: 100000, seconds: 3.029, > num_per_second: 33014.2 > : Serializing NewOrderSingle messages from > strings: > times-debug-normal.txt : num: 100000, seconds: 9.608, > num_per_second: 10408 > times-debug-modified.txt : num: 100000, seconds: 5.385, > num_per_second: 18570.1 > times-optimized-normal.txt : num: 100000, seconds: 7.202, > num_per_second: 13885 > times-optimized-modified.txt: num: 100000, seconds: 3.779, > num_per_second: 26462 > : Creating QuoteRequest messages: > times-debug-normal.txt : num: 100000, seconds: 93.295, > num_per_second: 1071.87 > times-debug-modified.txt : num: 100000, seconds: 46.975, > num_per_second: 2128.79 > times-optimized-normal.txt : num: 100000, seconds: 61.148, > num_per_second: 1635.38 > times-optimized-modified.txt: num: 100000, seconds: 33.904, > num_per_second: 2949.5 > : Serializing QuoteRequest messages to > strings: > times-debug-normal.txt : num: 100000, seconds: 11.052, > num_per_second: 9048.14 > times-debug-modified.txt : num: 100000, seconds: 10.691, > num_per_second: 9353.66 > times-optimized-normal.txt : num: 100000, seconds: 5.667, > num_per_second: 17646 > times-optimized-modified.txt: num: 100000, seconds: 4.808, > num_per_second: 20798.7 > : Serializing QuoteRequest messages from > strings: > times-debug-normal.txt : num: 100000, seconds: 74.91, > num_per_second: 1334.94 > times-debug-modified.txt : num: 100000, seconds: 48.556, > num_per_second: 2059.48 > times-optimized-normal.txt : num: 100000, seconds: 47.944, > num_per_second: 2085.77 > times-optimized-modified.txt: num: 100000, seconds: 28.627, > num_per_second: 3493.21 > : Reading fields from QuoteRequest message: > times-debug-normal.txt : num: 100000, seconds: 34.526, > num_per_second: 2896.37 > times-debug-modified.txt : num: 100000, seconds: 12.988, > num_per_second: 7699.41 > times-optimized-normal.txt : num: 100000, seconds: 21.891, > num_per_second: 4568.09 > times-optimized-modified.txt: num: 100000, seconds: 6.875, > num_per_second: 14545.5 > : Storing NewOrderSingle messages: > times-debug-normal.txt : num: 100000, seconds: 0.765, > num_per_second: 130719 > times-debug-modified.txt : num: 100000, seconds: 0.71, > num_per_second: 140845 > times-optimized-normal.txt : num: 100000, seconds: 0.575, > num_per_second: 173913 > times-optimized-modified.txt: num: 100000, seconds: 0.521, > num_per_second: 191939 > : Validating NewOrderSingle messages with > no data dictionary: > times-debug-normal.txt : num: 100000, seconds: 1.489, > num_per_second: 67159.2 > times-debug-modified.txt : num: 100000, seconds: 0.859, > num_per_second: 116414 > times-optimized-normal.txt : num: 100000, seconds: 0.843, > num_per_second: 118624 > times-optimized-modified.txt: num: 100000, seconds: 0.426, > num_per_second: 234742 > : Validating NewOrderSingle messages with > data dictionary: > times-debug-normal.txt : num: 100000, seconds: 7.732, > num_per_second: 12933.3 > times-debug-modified.txt : num: 100000, seconds: 6.105, > num_per_second: 16380 > times-optimized-normal.txt : num: 100000, seconds: 2.485, > num_per_second: 40241.4 > times-optimized-modified.txt: num: 100000, seconds: 1.717, > num_per_second: 58241.1 > > > --- quickfix/src/C++/FieldConvertors.h 2004-04-29 20:58:20.000000000 > +0100 > +++ quickfix-debug-modified/src/C++/FieldConvertors.h 2004-08-12 > 19:13:22.036829060 +0100 > @@ -1,3 +1,4 @@ > + > /* -*- C++ -*- */ > > > / > *********************************************************************** > ***** > @@ -32,6 +33,76 @@ > > namespace FIX > { > +template<class T> > +static inline char * integer_to_string (char * buf, const size_t len, > T t) > +{ > + const bool is_neg = t < 0; > + char * p = buf + len; > + > + *--p = '\0'; > + > + if (is_neg) { > + if (t == std::numeric_limits<T>::min()) { > + *--p = '0' + (10-t%10)%10; > + t/=10; > + } > + t=-t; > + do { > + *--p = '0' + t % 10; > + t /= 10; > + } while (t > 0); > + *--p = '-'; > + } else { > + do { > + *--p = '0' + t % 10; > + t /= 10; > + } while (t > 0); > + } > + return p; > +} > + > + > + > +template<class T> > +static inline char * integer_to_string_padded (char * buf, > + const size_t len, > + T t, > + const size_t width = 0, > + const char padding_char = '0') > +{ > + if (!width) return integer_to_string (buf, len, t); > + > + const bool is_neg = t < 0; > + char * p = buf + len; > + > + *--p = '\0'; > + > + if (is_neg) { > + if (t == std::numeric_limits<T>::min()) { > + *--p = '0' + (10-t%10)%10; > + t/=10; > + } > + t=-t; > + do { > + *--p = '0' + t % 10; > + t /= 10; > + } while (t > 0); > + if (p > buf) > + *--p = '-'; > + } else { > + do { > + *--p = '0' + t % 10; > + t /= 10; > + } while (t > 0); > + } > + const char * stop_p = buf + len - width - 1; > + if (stop_p < buf) stop_p = buf; > + while (p > stop_p) > + *--p = padding_char; > + return p; > +} > + > + > /// Empty convertor is a no-op. > struct EmptyConvertor > { > @@ -46,21 +117,32 @@ > { > static std::string convert( long value ) > { > - char temp[ 12 ]; > - memset( temp, 0, 12 * sizeof( char ) ); > - sprintf( temp, "%d", ( int ) value ); > - return temp; > + // buffer is big enough for significant digits and extra digit, > minus and null > + char buffer [std::numeric_limits<long>::digits10 + 3]; > + const char * const start = integer_to_string (buffer, sizeof > (buffer), value); > + return std::string (start, buffer + sizeof (buffer) - start - 1); > } > > static bool convert( const std::string& value, long& result ) > { > const char * str = value.c_str(); > - if ( *str == '-' ) ++str; > - for ( const char * p = str; *p != 0; ++p ) > - { > - if ( !isdigit( *p ) ) return false; > + bool is_neg = false; > + long x = 0; > + > + if (*str == '-') { > + is_neg = true; > + ++str; > } > - result = atol( value.c_str() ); > + > + do { > + const int c = *str - '0'; > + if (c < 0 || 9 < c) return false; > + x = 10 * x + c; > + } while (*++str); > + > + if (is_neg) x = -x; > + > + result = x; > return true; > } > > @@ -81,9 +163,10 @@ > throw( FieldConvertError& ) > { > if ( value > 255 || value < 0 ) throw FieldConvertError(); > - std::stringstream stream; > - stream << std::setw( 3 ) << std::setfill( '0' ) << value; > - return stream.str(); > + char result [4]; > + if (integer_to_string_padded (result, sizeof(result), value, 3, > '0') != result) > + throw FieldConvertError(); > + return std::string (result, 3); > } > > static bool convert( const std::string& value, long& result ) > @@ -104,53 +187,32 @@ > static std::string convert( double value ) > { > char result[ 32 ]; > - memset( result, 0, 32 * sizeof( char ) ); > - sprintf( result, "%.15g", value ); > - return result; > + return std::string (result, std::sprintf( result, "%.15g", value > )); > } > > static bool convert( const std::string& value, double& result ) > { > - if ( !value.size() ) return false; > - if ( value == "." ) return false; > - > - result = atof( const_cast < char* > ( value.c_str() ) ); > - std::string stripped = value; > - > - // add leading zero if none exists > - if ( *stripped.begin() == '.' ) > - stripped = '0' + stripped; > - > - // remove extra leading zeros > - while ( stripped.size() > 1 > - && *(stripped.begin()) == '0' > - && isdigit(*(stripped.begin()+1)) ) > - { > - stripped.erase( stripped.begin() ); > + const char * i = value.c_str(); > + > + if (!*i) return false; // Catch null > strings > + if (*i == '-' && !*++i) return false; // Eat leading > '-' and recheck for null string > + > + bool have_digit = false; > + > + if (std::isdigit (*i)) { > + have_digit = true; > + while (std::isdigit (*++i)); > } > > - if ( stripped.find( '.' ) != std::string::npos ) > - { > - std::string::reverse_iterator r = stripped.rbegin(); > - while ( r != stripped.rend() && ( *r == '0' || *r == '.' ) ) > - { > - if ( *r == '.' ) { stripped.resize( stripped.size() - 1 ); > break; } > - stripped.resize( stripped.size() - 1 ); r++; > - } > - } > - if ( *stripped.begin() == '0' ) > - { > - std::string::size_type i = stripped.find_first_not_of( '0' ); > - std::string::size_type dot = stripped.find( '.' ); > - if ( i != dot ) > - stripped = stripped.substr( i ); > + if (*i == '.' && std::isdigit (*++i)) { > + have_digit = true; > + while (std::isdigit (*++i)); > } > > - std::string converted = convert( result ); > - if ( stripped.size() != converted.size() ) return false; > + if (*i || !have_digit) return false; > > - // strcmp is being used because == operator is funky under linux > - return ( strcmp( stripped.c_str(), converted.c_str() ) == 0 ); > + result = std::strtod (value.c_str(), 0); > + return true; > } > > static double convert( const std::string& value ) > @@ -169,16 +231,13 @@ > { > static std::string convert( char value ) > { > - char temp[ 2 ]; > - temp[ 0 ] = value; > - temp[ 1 ] = '\0'; > - return temp; > + return std::string (1, value); > } > > static bool convert( const std::string& value, char& result ) > { > - if ( value.size() == 0 ) return false; > - result = *value.c_str(); > + if ( value.size() != 1 ) return false; > + result = value[0]; > return true; > } > > @@ -197,15 +256,19 @@ > struct BoolConvertor > { > static std::string convert( bool value ) > - { return value ? "Y" : "N"; } > + { > + const char ch = value ? 'Y' : 'N'; > + return std::string (1, ch); > + } > > static bool convert( const std::string& value, bool& result ) > { > - if ( value == "Y" ) result = true; > - else > - if ( value == "N" ) result = false; > - else > - return false; > + if (value.size() != 1) return false; > + switch (value[0]) { > + case 'Y': result = true; break; > + case 'N': result = false; break; > + default: return false; > + } > return true; > } > > @@ -227,13 +290,19 @@ > throw( FieldConvertError& ) > { > char result[ 18+4 ]; > - int len = strftime( result, 18, "%Y%m%d-%H:%M:%S", value ); > - if ( len != 17 ) throw FieldConvertError(); > + > + integer_to_string_padded (result , 5, static_cast<const > tm*>(value)->tm_year + 1900, 4, '0'); > + integer_to_string_padded (result + 4, 3, static_cast<const > tm*>(value)->tm_mon + 1, 2, '0'); > + integer_to_string_padded (result + 6, 3, static_cast<const > tm*>(value)->tm_mday, 2, '0'); result[8] = '-'; > + integer_to_string_padded (result + 9, 3, static_cast<const > tm*>(value)->tm_hour, 2, '0'); result[11] = ':'; > + integer_to_string_padded (result + 12, 3, static_cast<const > tm*>(value)->tm_min, 2, '0'); result[14] = ':'; > + integer_to_string_padded (result + 15, 3, static_cast<const > tm*>(value)->tm_sec, 2, '0'); > > if(true==showMilliseconds) > { > - len = sprintf(result+17,".%03d",value.getMillisecond()); > - if ( len != 4) throw FieldConvertError(); > + result[17] = '.'; > + if (integer_to_string_padded (result + 18, 4, > value.getMillisecond(), 3, '0') != result + 18) > + throw FieldConvertError(); > } > return result; > } > @@ -242,19 +311,73 @@ > throw( FieldConvertError& ) > { > UtcTimeStamp result; > - const char* val = value.c_str(); > - const char* len = strptime( val, "%Y%m%d-%H:%M:%S", result ); > - if ( len - val != 17 ) throw FieldConvertError(); > - > - // if we have milliseconds in the string, *len should be ".sss" > - result.setMillisecond(0); > - if(NULL != len && strlen(len)==4 && len[0] == '.') > - { > - int ms = atoi(&len[1]); > - if(ms < 0 || ms > 999) throw FieldConvertError(); > - result.setMillisecond(ms); > + bool have_milliseconds = false; > + > + switch (value.size()) { > + case 21: have_milliseconds = true; > + case 17: break; > + default: throw FieldConvertError(); > } > > + int i = 0; > + for (int c=0; c<8; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (value[i++] != '-') throw > FieldConvertError(); > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (value[i++] != ':') throw > FieldConvertError(); > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (value[i++] != ':') throw > FieldConvertError(); > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (have_milliseconds) { > + if (value[i++] != '.') throw FieldConvertError(); > + for (int c=0; c<3; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + } > + > + tm & result_tm = *static_cast<tm*>(result); > + > + i = 0; > + > + result_tm.tm_year = value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year -= 1900; > + > + result_tm.tm_mon = value[i++] - '0'; > + result_tm.tm_mon = 10 * result_tm.tm_mon + value[i++] - '0'; > + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw > FieldConvertError(); > + --result_tm.tm_mon; > + > + result_tm.tm_mday = value[i++] - '0'; > + result_tm.tm_mday = 10 * result_tm.tm_mday + value[i++] - '0'; > + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw > FieldConvertError(); > + > + ++i; // skip '-' > + > + result_tm.tm_hour = value[i++] - '0'; > + result_tm.tm_hour = 10 * result_tm.tm_hour + value[i++] - '0'; > + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No > check for >= 0 as no '-' are converted here > + > + ++i; // skip ':' > + > + result_tm.tm_min = value[i++] - '0'; > + result_tm.tm_min = 10 * result_tm.tm_min + value[i++] - '0'; > + if (59 < result_tm.tm_min) throw FieldConvertError(); // No check > for >= 0 as no '-' are converted here > + > + ++i; // skip ':' > + > + result_tm.tm_sec = value[i++] - '0'; > + result_tm.tm_sec = 10 * result_tm.tm_sec + value[i++] - '0'; > + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No check > for >= 0 as no '-' are converted here > + > + if (have_milliseconds) > + result.setMillisecond (100 * (value[i+1] - '0') + > + 10 * (value[i+2] - '0') + > + (value[i+3] - '0')); > + else > + result.setMillisecond (0); > + > + result_tm.tm_isdst = -1; > + > return result; > } > }; > @@ -266,13 +389,16 @@ > throw( FieldConvertError& ) > { > char result[ 9+4 ]; > - int len = strftime( result, 9, "%H:%M:%S", value ); > - if ( len != 8 ) throw FieldConvertError(); > + > + integer_to_string_padded (result , 3, static_cast<const > tm*>(value)->tm_hour, 2, '0'); result[2] = ':'; > + integer_to_string_padded (result + 3, 3, static_cast<const > tm*>(value)->tm_min, 2, '0'); result[5] = ':'; > + integer_to_string_padded (result + 6, 3, static_cast<const > tm*>(value)->tm_sec, 2, '0'); > > if(true==showMilliseconds) > { > - len = sprintf(result+8,".%03d",value.getMillisecond()); > - if ( len != 4) throw FieldConvertError(); > + result[8] = '.'; > + if (integer_to_string_padded (result + 9, 4, > value.getMillisecond(), 3, '0') != result + 9) > + throw FieldConvertError(); > } > > return result; > @@ -282,19 +408,52 @@ > throw( FieldConvertError& ) > { > UtcTimeOnly result; > - const char* val = value.c_str(); > - const char* len = strptime( val, "%H:%M:%S", result ); > - if ( len - val != 8 ) throw FieldConvertError(); > - > - // if we have milliseconds in the string, *len should be ".sss" > - result.setMillisecond(0); > - if(NULL != len && strlen(len)==4 && len[0] == '.') > - { > - int ms = atoi(&len[1]); > - if(ms < 0 || ms > 999) throw FieldConvertError(); > - result.setMillisecond(ms); > + bool have_milliseconds = false; > + > + switch (value.size()) { > + case 12: have_milliseconds = true; > + case 8: break; > + default: throw FieldConvertError(); > + } > + > + int i = 0; > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (value[i++] != ':') throw > FieldConvertError(); > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (value[i++] != ':') throw > FieldConvertError(); > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (have_milliseconds) { > + // ++i instead of i++ skips the '.' separator > + for (int c=0; c<3; ++c) if (!std::isdigit(value[++i])) throw > FieldConvertError(); > } > > + tm & result_tm = *static_cast<tm*>(result); > + > + i = 0; > + > + result_tm.tm_hour = value[i++] - '0'; > + result_tm.tm_hour = 10 * result_tm.tm_hour + value[i++] - '0'; > + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No > check for >= 0 as no '-' are converted here > + ++i; // skip ':' > + > + result_tm.tm_min = value[i++] - '0'; > + result_tm.tm_min = 10 * result_tm.tm_min + value[i++] - '0'; > + if (59 < result_tm.tm_min) throw FieldConvertError(); // No check > for >= 0 as no '-' are converted here > + ++i; // skip ':' > + > + result_tm.tm_sec = value[i++] - '0'; > + result_tm.tm_sec = 10 * result_tm.tm_sec + value[i++] - '0'; > + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No check > for >= 0 as no '-' are converted here > + > + if (have_milliseconds) > + result.setMillisecond (100 * (value[i+1] - '0') + > + 10 * (value[i+2] - '0') + > + (value[i+3] - '0')); > + else > + result.setMillisecond (0); > + > + result_tm.tm_isdst = -1; > + > return result; > } > }; > @@ -306,8 +465,9 @@ > throw( FieldConvertError& ) > { > char result[ 9 ]; > - int len = strftime( result, 9, "%Y%m%d", value ); > - if ( len != 8 ) throw FieldConvertError(); > + integer_to_string_padded (result , 5, static_cast<const > tm*>(value)->tm_year + 1900, 4, '0'); > + integer_to_string_padded (result + 4, 3, static_cast<const > tm*>(value)->tm_mon + 1, 2, '0'); > + integer_to_string_padded (result + 6, 3, static_cast<const > tm*>(value)->tm_mday, 2, '0'); > return result; > } > > @@ -315,9 +475,34 @@ > throw( FieldConvertError& ) > { > UtcDate result; > - const char* val = value.c_str(); > - const char* len = strptime( val, "%Y%m%d", result ); > - if ( len - val != 8 ) throw FieldConvertError(); > + > + if (value.size() != 8) throw FieldConvertError(); > + > + int i = 0; > + for (int c=0; c<8; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + > + tm & result_tm = *static_cast<tm*>(result); > + > + i = 0; > + > + result_tm.tm_year = value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year -= 1900; > + > + result_tm.tm_mon = value[i++] - '0'; > + result_tm.tm_mon = 10 * result_tm.tm_mon + value[i++] - '0'; > + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw > FieldConvertError(); > + --result_tm.tm_mon; > + > + result_tm.tm_mday = value[i++] - '0'; > + result_tm.tm_mday = 10 * result_tm.tm_mday + value[i++] - '0'; > + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw > FieldConvertError(); > + ++i; // skip '-' > + > + result_tm.tm_isdst = -1; > + > return result; > } > }; > > > <font face="Times New Roman" size="3"> > <p>-------------------------------------------------------------------- > ----------</p> > <p> This email is intended only for the use of the individual(s) to > whom it is addressed and may be privileged and confidential. > Unauthorised use or disclosure is prohibited. If you receive this > e-mail in error, please advise immediately and delete the original > message. This message may have been altered without your or our > knowledge and the sender does not accept any liability for any errors > or omissions in the message.</p> > <p>====================================================</p> > </font> > > > > ------------------------------------------------------- > SF.Net email is sponsored by Shop4tech.com-Lowest price on Blank Media > 100pk Sonic DVD-R 4x for only $29 -100pk Sonic DVD+R for only $33 > Save 50% off Retail on Ink & Toner - Free Shipping and Free Gift. > http://www.shop4tech.com/z/Inkjet_Cartridges/9_108_r285 > _______________________________________________ > Quickfix-developers mailing list > Qui...@li... > https://lists.sourceforge.net/lists/listinfo/quickfix-developers > |
From: Oren M. <or...@qu...> - 2004-08-13 16:28:47
|
Very silly of me, I was looking at the wrong numbers. My results look similar to yours. I've checked in the changes. --oren On Aug 13, 2004, at 10:51 AM, Oren Miller wrote: > Thanks Kristopher, > > I ran these through and was able to see nice performance improvements > on serializing to strings, however for some reasons the performance > for serializing from strings remained unchanged on the systems I > tested under. Also converting from doubles to strings showed no > improvement. I tried this under a i686 Xeon and a powerpc chipsets. > > --oren > > On Aug 12, 2004, at 2:13 PM, Peterson, Kristofer wrote: > >> QuickFIX Documentation: >> http://www.quickfixengine.org/quickfix/doc/html/index.html >> QuickFIX FAQ: >> http://www.quickfixengine.org/wikifix/index.php?QuickFixFAQ >> QuickFIX Support: http://www.quickfixengine.org/services.html >> >> I have optimized some of the field convertors in FieldConvertors.h >> and have achieved substantial improvements in performance (generally >> 50% - 200% faster). I include a patch against the FieldConvertors.h >> file from quickfix-1.7.1. >> >> The code is admittedly 'C-ish', however no interface has been changed >> and I believe the results speak for themselves. >> >> Replacing strtod() in the double to string conversion would probably >> be a significant win, although I don't know of any efficient >> algorithms for this conversion. >> >> Finally, time stamp conversions should probably be added to pt as >> well. >> >> Cheers, >> >> - Kris >> >> The times were obtained from pt running on an AthlonXP 2500 running >> Linux kernel 2.6.7 compiled with g++ 3.3.4. 'normal' builds used the >> existing FieldConvertors.h, 'modified' builds used the modified >> FieldConvertors.h. >> >> Optimization flags for the builds were as follows: >> >> quickfix-debug-modified/Makefile:CFLAGS = -g -Wall >> quickfix-debug-normal/Makefile:CFLAGS = -g -Wall >> quickfix-optimized-modified/Makefile:CFLAGS = -g -O3 -ffast-math >> -mcpu=athlon-xp -Wall >> quickfix-optimized-normal/Makefile:CFLAGS = -g -O3 -ffast-math >> -mcpu=athlon-xp -Wall >> >> times-debug-normal.txt : peterson@karl src $ ./pt -c 100000 >> times-debug-modified.txt : peterson@karl src $ ./pt -c 100000 >> times-optimized-normal.txt : peterson@karl src $ ./pt -c 100000 >> times-optimized-modified.txt: peterson@karl src $ ./pt -c 100000 >> >> : Converting integers to strings: >> times-debug-normal.txt : num: 100000, seconds: 0.259, >> num_per_second: 386100 >> times-debug-modified.txt : num: 100000, seconds: 0.032, >> num_per_second: 3.125e+06 >> times-optimized-normal.txt : num: 100000, seconds: 0.183, >> num_per_second: 546448 >> times-optimized-modified.txt: num: 100000, seconds: 0.026, >> num_per_second: 3.84615e+06 >> : Converting strings to integers: >> times-debug-normal.txt : num: 100000, seconds: 0.024, >> num_per_second: 4.16667e+06 >> times-debug-modified.txt : num: 100000, seconds: 0.008, >> num_per_second: 1.25e+07 >> times-optimized-normal.txt : num: 100000, seconds: 0.016, >> num_per_second: 6.25e+06 >> times-optimized-modified.txt: num: 100000, seconds: 0.004, >> num_per_second: 2.5e+07 >> : Converting doubles to strings: >> times-debug-normal.txt : num: 100000, seconds: 0.711, >> num_per_second: 140647 >> times-debug-modified.txt : num: 100000, seconds: 0.243, >> num_per_second: 411523 >> times-optimized-normal.txt : num: 100000, seconds: 0.531, >> num_per_second: 188324 >> times-optimized-modified.txt: num: 100000, seconds: 0.236, >> num_per_second: 423729 >> : Converting strings to doubles: >> times-debug-normal.txt : num: 100000, seconds: 0.987, >> num_per_second: 101317 >> times-debug-modified.txt : num: 100000, seconds: 0.058, >> num_per_second: 1.72414e+06 >> times-optimized-normal.txt : num: 100000, seconds: 0.725, >> num_per_second: 137931 >> times-optimized-modified.txt: num: 100000, seconds: 0.062, >> num_per_second: 1.6129e+06 >> : Creating Heartbeat messages: >> times-debug-normal.txt : num: 100000, seconds: 1.823, >> num_per_second: 54854.6 >> times-debug-modified.txt : num: 100000, seconds: 1.166, >> num_per_second: 85763.3 >> times-optimized-normal.txt : num: 100000, seconds: 1.242, >> num_per_second: 80515.3 >> times-optimized-modified.txt: num: 100000, seconds: 0.825, >> num_per_second: 121212 >> : Serializing Heartbeat messages to >> strings: >> times-debug-normal.txt : num: 100000, seconds: 2.35, >> num_per_second: 42553.2 >> times-debug-modified.txt : num: 100000, seconds: 1.198, >> num_per_second: 83472.5 >> times-optimized-normal.txt : num: 100000, seconds: 1.425, >> num_per_second: 70175.4 >> times-optimized-modified.txt: num: 100000, seconds: 0.707, >> num_per_second: 141443 >> : Serializing Heartbeat messages from >> strings: >> times-debug-normal.txt : num: 100000, seconds: 4.749, >> num_per_second: 21057.1 >> times-debug-modified.txt : num: 100000, seconds: 2.671, >> num_per_second: 37439.2 >> times-optimized-normal.txt : num: 100000, seconds: 3.053, >> num_per_second: 32754.7 >> times-optimized-modified.txt: num: 100000, seconds: 1.782, >> num_per_second: 56116.7 >> : Creating NewOrderSingle messages: >> times-debug-normal.txt : num: 100000, seconds: 6.422, >> num_per_second: 15571.5 >> times-debug-modified.txt : num: 100000, seconds: 3.937, >> num_per_second: 25400.1 >> times-optimized-normal.txt : num: 100000, seconds: 4.778, >> num_per_second: 20929.3 >> times-optimized-modified.txt: num: 100000, seconds: 3.026, >> num_per_second: 33046.9 >> : Serializing NewOrderSingle messages to >> strings: >> times-debug-normal.txt : num: 100000, seconds: 6.429, >> num_per_second: 15554.5 >> times-debug-modified.txt : num: 100000, seconds: 3.84, >> num_per_second: 26041.7 >> times-optimized-normal.txt : num: 100000, seconds: 4.874, >> num_per_second: 20517 >> times-optimized-modified.txt: num: 100000, seconds: 3.029, >> num_per_second: 33014.2 >> : Serializing NewOrderSingle messages >> from strings: >> times-debug-normal.txt : num: 100000, seconds: 9.608, >> num_per_second: 10408 >> times-debug-modified.txt : num: 100000, seconds: 5.385, >> num_per_second: 18570.1 >> times-optimized-normal.txt : num: 100000, seconds: 7.202, >> num_per_second: 13885 >> times-optimized-modified.txt: num: 100000, seconds: 3.779, >> num_per_second: 26462 >> : Creating QuoteRequest messages: >> times-debug-normal.txt : num: 100000, seconds: 93.295, >> num_per_second: 1071.87 >> times-debug-modified.txt : num: 100000, seconds: 46.975, >> num_per_second: 2128.79 >> times-optimized-normal.txt : num: 100000, seconds: 61.148, >> num_per_second: 1635.38 >> times-optimized-modified.txt: num: 100000, seconds: 33.904, >> num_per_second: 2949.5 >> : Serializing QuoteRequest messages to >> strings: >> times-debug-normal.txt : num: 100000, seconds: 11.052, >> num_per_second: 9048.14 >> times-debug-modified.txt : num: 100000, seconds: 10.691, >> num_per_second: 9353.66 >> times-optimized-normal.txt : num: 100000, seconds: 5.667, >> num_per_second: 17646 >> times-optimized-modified.txt: num: 100000, seconds: 4.808, >> num_per_second: 20798.7 >> : Serializing QuoteRequest messages from >> strings: >> times-debug-normal.txt : num: 100000, seconds: 74.91, >> num_per_second: 1334.94 >> times-debug-modified.txt : num: 100000, seconds: 48.556, >> num_per_second: 2059.48 >> times-optimized-normal.txt : num: 100000, seconds: 47.944, >> num_per_second: 2085.77 >> times-optimized-modified.txt: num: 100000, seconds: 28.627, >> num_per_second: 3493.21 >> : Reading fields from QuoteRequest >> message: >> times-debug-normal.txt : num: 100000, seconds: 34.526, >> num_per_second: 2896.37 >> times-debug-modified.txt : num: 100000, seconds: 12.988, >> num_per_second: 7699.41 >> times-optimized-normal.txt : num: 100000, seconds: 21.891, >> num_per_second: 4568.09 >> times-optimized-modified.txt: num: 100000, seconds: 6.875, >> num_per_second: 14545.5 >> : Storing NewOrderSingle messages: >> times-debug-normal.txt : num: 100000, seconds: 0.765, >> num_per_second: 130719 >> times-debug-modified.txt : num: 100000, seconds: 0.71, >> num_per_second: 140845 >> times-optimized-normal.txt : num: 100000, seconds: 0.575, >> num_per_second: 173913 >> times-optimized-modified.txt: num: 100000, seconds: 0.521, >> num_per_second: 191939 >> : Validating NewOrderSingle messages with >> no data dictionary: >> times-debug-normal.txt : num: 100000, seconds: 1.489, >> num_per_second: 67159.2 >> times-debug-modified.txt : num: 100000, seconds: 0.859, >> num_per_second: 116414 >> times-optimized-normal.txt : num: 100000, seconds: 0.843, >> num_per_second: 118624 >> times-optimized-modified.txt: num: 100000, seconds: 0.426, >> num_per_second: 234742 >> : Validating NewOrderSingle messages with >> data dictionary: >> times-debug-normal.txt : num: 100000, seconds: 7.732, >> num_per_second: 12933.3 >> times-debug-modified.txt : num: 100000, seconds: 6.105, >> num_per_second: 16380 >> times-optimized-normal.txt : num: 100000, seconds: 2.485, >> num_per_second: 40241.4 >> times-optimized-modified.txt: num: 100000, seconds: 1.717, >> num_per_second: 58241.1 >> >> >> --- quickfix/src/C++/FieldConvertors.h 2004-04-29 20:58:20.000000000 >> +0100 >> +++ quickfix-debug-modified/src/C++/FieldConvertors.h 2004-08-12 >> 19:13:22.036829060 +0100 >> @@ -1,3 +1,4 @@ >> + >> /* -*- C++ -*- */ >> >> >> / >> ********************************************************************** >> ****** >> @@ -32,6 +33,76 @@ >> >> namespace FIX >> { >> +template<class T> >> +static inline char * integer_to_string (char * buf, const size_t >> len, T t) >> +{ >> + const bool is_neg = t < 0; >> + char * p = buf + len; >> + >> + *--p = '\0'; >> + >> + if (is_neg) { >> + if (t == std::numeric_limits<T>::min()) { >> + *--p = '0' + (10-t%10)%10; >> + t/=10; >> + } >> + t=-t; >> + do { >> + *--p = '0' + t % 10; >> + t /= 10; >> + } while (t > 0); >> + *--p = '-'; >> + } else { >> + do { >> + *--p = '0' + t % 10; >> + t /= 10; >> + } while (t > 0); >> + } >> + return p; >> +} >> + >> + >> + >> +template<class T> >> +static inline char * integer_to_string_padded (char * buf, >> + const size_t len, >> + T t, >> + const size_t width = 0, >> + const char padding_char = '0') >> +{ >> + if (!width) return integer_to_string (buf, len, t); >> + >> + const bool is_neg = t < 0; >> + char * p = buf + len; >> + >> + *--p = '\0'; >> + >> + if (is_neg) { >> + if (t == std::numeric_limits<T>::min()) { >> + *--p = '0' + (10-t%10)%10; >> + t/=10; >> + } >> + t=-t; >> + do { >> + *--p = '0' + t % 10; >> + t /= 10; >> + } while (t > 0); >> + if (p > buf) >> + *--p = '-'; >> + } else { >> + do { >> + *--p = '0' + t % 10; >> + t /= 10; >> + } while (t > 0); >> + } >> + const char * stop_p = buf + len - width - 1; >> + if (stop_p < buf) stop_p = buf; >> + while (p > stop_p) >> + *--p = padding_char; >> + return p; >> +} >> + >> + >> /// Empty convertor is a no-op. >> struct EmptyConvertor >> { >> @@ -46,21 +117,32 @@ >> { >> static std::string convert( long value ) >> { >> - char temp[ 12 ]; >> - memset( temp, 0, 12 * sizeof( char ) ); >> - sprintf( temp, "%d", ( int ) value ); >> - return temp; >> + // buffer is big enough for significant digits and extra digit, >> minus and null >> + char buffer [std::numeric_limits<long>::digits10 + 3]; >> + const char * const start = integer_to_string (buffer, sizeof >> (buffer), value); >> + return std::string (start, buffer + sizeof (buffer) - start - 1); >> } >> >> static bool convert( const std::string& value, long& result ) >> { >> const char * str = value.c_str(); >> - if ( *str == '-' ) ++str; >> - for ( const char * p = str; *p != 0; ++p ) >> - { >> - if ( !isdigit( *p ) ) return false; >> + bool is_neg = false; >> + long x = 0; >> + >> + if (*str == '-') { >> + is_neg = true; >> + ++str; >> } >> - result = atol( value.c_str() ); >> + >> + do { >> + const int c = *str - '0'; >> + if (c < 0 || 9 < c) return false; >> + x = 10 * x + c; >> + } while (*++str); >> + >> + if (is_neg) x = -x; >> + >> + result = x; >> return true; >> } >> >> @@ -81,9 +163,10 @@ >> throw( FieldConvertError& ) >> { >> if ( value > 255 || value < 0 ) throw FieldConvertError(); >> - std::stringstream stream; >> - stream << std::setw( 3 ) << std::setfill( '0' ) << value; >> - return stream.str(); >> + char result [4]; >> + if (integer_to_string_padded (result, sizeof(result), value, 3, >> '0') != result) >> + throw FieldConvertError(); >> + return std::string (result, 3); >> } >> >> static bool convert( const std::string& value, long& result ) >> @@ -104,53 +187,32 @@ >> static std::string convert( double value ) >> { >> char result[ 32 ]; >> - memset( result, 0, 32 * sizeof( char ) ); >> - sprintf( result, "%.15g", value ); >> - return result; >> + return std::string (result, std::sprintf( result, "%.15g", value >> )); >> } >> >> static bool convert( const std::string& value, double& result ) >> { >> - if ( !value.size() ) return false; >> - if ( value == "." ) return false; >> - >> - result = atof( const_cast < char* > ( value.c_str() ) ); >> - std::string stripped = value; >> - >> - // add leading zero if none exists >> - if ( *stripped.begin() == '.' ) >> - stripped = '0' + stripped; >> - >> - // remove extra leading zeros >> - while ( stripped.size() > 1 >> - && *(stripped.begin()) == '0' >> - && isdigit(*(stripped.begin()+1)) ) >> - { >> - stripped.erase( stripped.begin() ); >> + const char * i = value.c_str(); >> + >> + if (!*i) return false; // Catch null >> strings >> + if (*i == '-' && !*++i) return false; // Eat >> leading '-' and recheck for null string >> + >> + bool have_digit = false; >> + >> + if (std::isdigit (*i)) { >> + have_digit = true; >> + while (std::isdigit (*++i)); >> } >> >> - if ( stripped.find( '.' ) != std::string::npos ) >> - { >> - std::string::reverse_iterator r = stripped.rbegin(); >> - while ( r != stripped.rend() && ( *r == '0' || *r == '.' ) ) >> - { >> - if ( *r == '.' ) { stripped.resize( stripped.size() - 1 ); >> break; } >> - stripped.resize( stripped.size() - 1 ); r++; >> - } >> - } >> - if ( *stripped.begin() == '0' ) >> - { >> - std::string::size_type i = stripped.find_first_not_of( '0' ); >> - std::string::size_type dot = stripped.find( '.' ); >> - if ( i != dot ) >> - stripped = stripped.substr( i ); >> + if (*i == '.' && std::isdigit (*++i)) { >> + have_digit = true; >> + while (std::isdigit (*++i)); >> } >> >> - std::string converted = convert( result ); >> - if ( stripped.size() != converted.size() ) return false; >> + if (*i || !have_digit) return false; >> >> - // strcmp is being used because == operator is funky under linux >> - return ( strcmp( stripped.c_str(), converted.c_str() ) == 0 ); >> + result = std::strtod (value.c_str(), 0); >> + return true; >> } >> >> static double convert( const std::string& value ) >> @@ -169,16 +231,13 @@ >> { >> static std::string convert( char value ) >> { >> - char temp[ 2 ]; >> - temp[ 0 ] = value; >> - temp[ 1 ] = '\0'; >> - return temp; >> + return std::string (1, value); >> } >> >> static bool convert( const std::string& value, char& result ) >> { >> - if ( value.size() == 0 ) return false; >> - result = *value.c_str(); >> + if ( value.size() != 1 ) return false; >> + result = value[0]; >> return true; >> } >> >> @@ -197,15 +256,19 @@ >> struct BoolConvertor >> { >> static std::string convert( bool value ) >> - { return value ? "Y" : "N"; } >> + { >> + const char ch = value ? 'Y' : 'N'; >> + return std::string (1, ch); >> + } >> >> static bool convert( const std::string& value, bool& result ) >> { >> - if ( value == "Y" ) result = true; >> - else >> - if ( value == "N" ) result = false; >> - else >> - return false; >> + if (value.size() != 1) return false; >> + switch (value[0]) { >> + case 'Y': result = true; break; >> + case 'N': result = false; break; >> + default: return false; >> + } >> return true; >> } >> >> @@ -227,13 +290,19 @@ >> throw( FieldConvertError& ) >> { >> char result[ 18+4 ]; >> - int len = strftime( result, 18, "%Y%m%d-%H:%M:%S", value ); >> - if ( len != 17 ) throw FieldConvertError(); >> + >> + integer_to_string_padded (result , 5, static_cast<const >> tm*>(value)->tm_year + 1900, 4, '0'); >> + integer_to_string_padded (result + 4, 3, static_cast<const >> tm*>(value)->tm_mon + 1, 2, '0'); >> + integer_to_string_padded (result + 6, 3, static_cast<const >> tm*>(value)->tm_mday, 2, '0'); result[8] = '-'; >> + integer_to_string_padded (result + 9, 3, static_cast<const >> tm*>(value)->tm_hour, 2, '0'); result[11] = ':'; >> + integer_to_string_padded (result + 12, 3, static_cast<const >> tm*>(value)->tm_min, 2, '0'); result[14] = ':'; >> + integer_to_string_padded (result + 15, 3, static_cast<const >> tm*>(value)->tm_sec, 2, '0'); >> >> if(true==showMilliseconds) >> { >> - len = sprintf(result+17,".%03d",value.getMillisecond()); >> - if ( len != 4) throw FieldConvertError(); >> + result[17] = '.'; >> + if (integer_to_string_padded (result + 18, 4, >> value.getMillisecond(), 3, '0') != result + 18) >> + throw FieldConvertError(); >> } >> return result; >> } >> @@ -242,19 +311,73 @@ >> throw( FieldConvertError& ) >> { >> UtcTimeStamp result; >> - const char* val = value.c_str(); >> - const char* len = strptime( val, "%Y%m%d-%H:%M:%S", result ); >> - if ( len - val != 17 ) throw FieldConvertError(); >> - >> - // if we have milliseconds in the string, *len should be ".sss" >> - result.setMillisecond(0); >> - if(NULL != len && strlen(len)==4 && len[0] == '.') >> - { >> - int ms = atoi(&len[1]); >> - if(ms < 0 || ms > 999) throw FieldConvertError(); >> - result.setMillisecond(ms); >> + bool have_milliseconds = false; >> + >> + switch (value.size()) { >> + case 21: have_milliseconds = true; >> + case 17: break; >> + default: throw FieldConvertError(); >> } >> >> + int i = 0; >> + for (int c=0; c<8; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (value[i++] != '-') throw >> FieldConvertError(); >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (value[i++] != ':') throw >> FieldConvertError(); >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (value[i++] != ':') throw >> FieldConvertError(); >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (have_milliseconds) { >> + if (value[i++] != '.') throw FieldConvertError(); >> + for (int c=0; c<3; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + } >> + >> + tm & result_tm = *static_cast<tm*>(result); >> + >> + i = 0; >> + >> + result_tm.tm_year = value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year -= 1900; >> + >> + result_tm.tm_mon = value[i++] - '0'; >> + result_tm.tm_mon = 10 * result_tm.tm_mon + value[i++] - '0'; >> + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw >> FieldConvertError(); >> + --result_tm.tm_mon; >> + >> + result_tm.tm_mday = value[i++] - '0'; >> + result_tm.tm_mday = 10 * result_tm.tm_mday + value[i++] - '0'; >> + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw >> FieldConvertError(); >> + >> + ++i; // skip '-' >> + >> + result_tm.tm_hour = value[i++] - '0'; >> + result_tm.tm_hour = 10 * result_tm.tm_hour + value[i++] - '0'; >> + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + >> + ++i; // skip ':' >> + >> + result_tm.tm_min = value[i++] - '0'; >> + result_tm.tm_min = 10 * result_tm.tm_min + value[i++] - '0'; >> + if (59 < result_tm.tm_min) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + >> + ++i; // skip ':' >> + >> + result_tm.tm_sec = value[i++] - '0'; >> + result_tm.tm_sec = 10 * result_tm.tm_sec + value[i++] - '0'; >> + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + >> + if (have_milliseconds) >> + result.setMillisecond (100 * (value[i+1] - '0') + >> + 10 * (value[i+2] - '0') + >> + (value[i+3] - '0')); >> + else >> + result.setMillisecond (0); >> + >> + result_tm.tm_isdst = -1; >> + >> return result; >> } >> }; >> @@ -266,13 +389,16 @@ >> throw( FieldConvertError& ) >> { >> char result[ 9+4 ]; >> - int len = strftime( result, 9, "%H:%M:%S", value ); >> - if ( len != 8 ) throw FieldConvertError(); >> + >> + integer_to_string_padded (result , 3, static_cast<const >> tm*>(value)->tm_hour, 2, '0'); result[2] = ':'; >> + integer_to_string_padded (result + 3, 3, static_cast<const >> tm*>(value)->tm_min, 2, '0'); result[5] = ':'; >> + integer_to_string_padded (result + 6, 3, static_cast<const >> tm*>(value)->tm_sec, 2, '0'); >> >> if(true==showMilliseconds) >> { >> - len = sprintf(result+8,".%03d",value.getMillisecond()); >> - if ( len != 4) throw FieldConvertError(); >> + result[8] = '.'; >> + if (integer_to_string_padded (result + 9, 4, >> value.getMillisecond(), 3, '0') != result + 9) >> + throw FieldConvertError(); >> } >> >> return result; >> @@ -282,19 +408,52 @@ >> throw( FieldConvertError& ) >> { >> UtcTimeOnly result; >> - const char* val = value.c_str(); >> - const char* len = strptime( val, "%H:%M:%S", result ); >> - if ( len - val != 8 ) throw FieldConvertError(); >> - >> - // if we have milliseconds in the string, *len should be ".sss" >> - result.setMillisecond(0); >> - if(NULL != len && strlen(len)==4 && len[0] == '.') >> - { >> - int ms = atoi(&len[1]); >> - if(ms < 0 || ms > 999) throw FieldConvertError(); >> - result.setMillisecond(ms); >> + bool have_milliseconds = false; >> + >> + switch (value.size()) { >> + case 12: have_milliseconds = true; >> + case 8: break; >> + default: throw FieldConvertError(); >> + } >> + >> + int i = 0; >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (value[i++] != ':') throw >> FieldConvertError(); >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (value[i++] != ':') throw >> FieldConvertError(); >> + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + if (have_milliseconds) { >> + // ++i instead of i++ skips the '.' separator >> + for (int c=0; c<3; ++c) if (!std::isdigit(value[++i])) throw >> FieldConvertError(); >> } >> >> + tm & result_tm = *static_cast<tm*>(result); >> + >> + i = 0; >> + >> + result_tm.tm_hour = value[i++] - '0'; >> + result_tm.tm_hour = 10 * result_tm.tm_hour + value[i++] - '0'; >> + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + ++i; // skip ':' >> + >> + result_tm.tm_min = value[i++] - '0'; >> + result_tm.tm_min = 10 * result_tm.tm_min + value[i++] - '0'; >> + if (59 < result_tm.tm_min) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + ++i; // skip ':' >> + >> + result_tm.tm_sec = value[i++] - '0'; >> + result_tm.tm_sec = 10 * result_tm.tm_sec + value[i++] - '0'; >> + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No >> check for >= 0 as no '-' are converted here >> + >> + if (have_milliseconds) >> + result.setMillisecond (100 * (value[i+1] - '0') + >> + 10 * (value[i+2] - '0') + >> + (value[i+3] - '0')); >> + else >> + result.setMillisecond (0); >> + >> + result_tm.tm_isdst = -1; >> + >> return result; >> } >> }; >> @@ -306,8 +465,9 @@ >> throw( FieldConvertError& ) >> { >> char result[ 9 ]; >> - int len = strftime( result, 9, "%Y%m%d", value ); >> - if ( len != 8 ) throw FieldConvertError(); >> + integer_to_string_padded (result , 5, static_cast<const >> tm*>(value)->tm_year + 1900, 4, '0'); >> + integer_to_string_padded (result + 4, 3, static_cast<const >> tm*>(value)->tm_mon + 1, 2, '0'); >> + integer_to_string_padded (result + 6, 3, static_cast<const >> tm*>(value)->tm_mday, 2, '0'); >> return result; >> } >> >> @@ -315,9 +475,34 @@ >> throw( FieldConvertError& ) >> { >> UtcDate result; >> - const char* val = value.c_str(); >> - const char* len = strptime( val, "%Y%m%d", result ); >> - if ( len - val != 8 ) throw FieldConvertError(); >> + >> + if (value.size() != 8) throw FieldConvertError(); >> + >> + int i = 0; >> + for (int c=0; c<8; ++c) if (!std::isdigit(value[i++])) throw >> FieldConvertError(); >> + >> + tm & result_tm = *static_cast<tm*>(result); >> + >> + i = 0; >> + >> + result_tm.tm_year = value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; >> + result_tm.tm_year -= 1900; >> + >> + result_tm.tm_mon = value[i++] - '0'; >> + result_tm.tm_mon = 10 * result_tm.tm_mon + value[i++] - '0'; >> + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw >> FieldConvertError(); >> + --result_tm.tm_mon; >> + >> + result_tm.tm_mday = value[i++] - '0'; >> + result_tm.tm_mday = 10 * result_tm.tm_mday + value[i++] - '0'; >> + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw >> FieldConvertError(); >> + ++i; // skip '-' >> + >> + result_tm.tm_isdst = -1; >> + >> return result; >> } >> }; >> >> >> <font face="Times New Roman" size="3"> >> <p>------------------------------------------------------------------- >> -----------</p> >> <p> This email is intended only for the use of the individual(s) to >> whom it is addressed and may be privileged and confidential. >> Unauthorised use or disclosure is prohibited. If you receive this >> e-mail in error, please advise immediately and delete the original >> message. This message may have been altered without your or our >> knowledge and the sender does not accept any liability for any errors >> or omissions in the message.</p> >> <p>====================================================</p> >> </font> >> >> >> >> ------------------------------------------------------- >> SF.Net email is sponsored by Shop4tech.com-Lowest price on Blank Media >> 100pk Sonic DVD-R 4x for only $29 -100pk Sonic DVD+R for only $33 >> Save 50% off Retail on Ink & Toner - Free Shipping and Free Gift. >> http://www.shop4tech.com/z/Inkjet_Cartridges/9_108_r285 >> _______________________________________________ >> Quickfix-developers mailing list >> Qui...@li... >> https://lists.sourceforge.net/lists/listinfo/quickfix-developers >> > > > > ------------------------------------------------------- > SF.Net email is sponsored by Shop4tech.com-Lowest price on Blank Media > 100pk Sonic DVD-R 4x for only $29 -100pk Sonic DVD+R for only $33 > Save 50% off Retail on Ink & Toner - Free Shipping and Free Gift. > http://www.shop4tech.com/z/Inkjet_Cartridges/9_108_r285 > _______________________________________________ > Quickfix-developers mailing list > Qui...@li... > https://lists.sourceforge.net/lists/listinfo/quickfix-developers > |
From: Oren M. <or...@qu...> - 2004-08-13 15:53:45
|
BTW those comparisons are against what was in the repository, which may have something to do with it. They were already optimized a great deal from 1.7.1. But I'm still not sure why the double conversions didn't improve since that was unchanged from 1.7.1 --oren On Aug 12, 2004, at 2:13 PM, Peterson, Kristofer wrote: > QuickFIX Documentation: > http://www.quickfixengine.org/quickfix/doc/html/index.html > QuickFIX FAQ: > http://www.quickfixengine.org/wikifix/index.php?QuickFixFAQ > QuickFIX Support: http://www.quickfixengine.org/services.html > > I have optimized some of the field convertors in FieldConvertors.h and > have achieved substantial improvements in performance (generally 50% - > 200% faster). I include a patch against the FieldConvertors.h file > from quickfix-1.7.1. > > The code is admittedly 'C-ish', however no interface has been changed > and I believe the results speak for themselves. > > Replacing strtod() in the double to string conversion would probably > be a significant win, although I don't know of any efficient > algorithms for this conversion. > > Finally, time stamp conversions should probably be added to pt as well. > > Cheers, > > - Kris > > The times were obtained from pt running on an AthlonXP 2500 running > Linux kernel 2.6.7 compiled with g++ 3.3.4. 'normal' builds used the > existing FieldConvertors.h, 'modified' builds used the modified > FieldConvertors.h. > > Optimization flags for the builds were as follows: > > quickfix-debug-modified/Makefile:CFLAGS = -g -Wall > quickfix-debug-normal/Makefile:CFLAGS = -g -Wall > quickfix-optimized-modified/Makefile:CFLAGS = -g -O3 -ffast-math > -mcpu=athlon-xp -Wall > quickfix-optimized-normal/Makefile:CFLAGS = -g -O3 -ffast-math > -mcpu=athlon-xp -Wall > > times-debug-normal.txt : peterson@karl src $ ./pt -c 100000 > times-debug-modified.txt : peterson@karl src $ ./pt -c 100000 > times-optimized-normal.txt : peterson@karl src $ ./pt -c 100000 > times-optimized-modified.txt: peterson@karl src $ ./pt -c 100000 > > : Converting integers to strings: > times-debug-normal.txt : num: 100000, seconds: 0.259, > num_per_second: 386100 > times-debug-modified.txt : num: 100000, seconds: 0.032, > num_per_second: 3.125e+06 > times-optimized-normal.txt : num: 100000, seconds: 0.183, > num_per_second: 546448 > times-optimized-modified.txt: num: 100000, seconds: 0.026, > num_per_second: 3.84615e+06 > : Converting strings to integers: > times-debug-normal.txt : num: 100000, seconds: 0.024, > num_per_second: 4.16667e+06 > times-debug-modified.txt : num: 100000, seconds: 0.008, > num_per_second: 1.25e+07 > times-optimized-normal.txt : num: 100000, seconds: 0.016, > num_per_second: 6.25e+06 > times-optimized-modified.txt: num: 100000, seconds: 0.004, > num_per_second: 2.5e+07 > : Converting doubles to strings: > times-debug-normal.txt : num: 100000, seconds: 0.711, > num_per_second: 140647 > times-debug-modified.txt : num: 100000, seconds: 0.243, > num_per_second: 411523 > times-optimized-normal.txt : num: 100000, seconds: 0.531, > num_per_second: 188324 > times-optimized-modified.txt: num: 100000, seconds: 0.236, > num_per_second: 423729 > : Converting strings to doubles: > times-debug-normal.txt : num: 100000, seconds: 0.987, > num_per_second: 101317 > times-debug-modified.txt : num: 100000, seconds: 0.058, > num_per_second: 1.72414e+06 > times-optimized-normal.txt : num: 100000, seconds: 0.725, > num_per_second: 137931 > times-optimized-modified.txt: num: 100000, seconds: 0.062, > num_per_second: 1.6129e+06 > : Creating Heartbeat messages: > times-debug-normal.txt : num: 100000, seconds: 1.823, > num_per_second: 54854.6 > times-debug-modified.txt : num: 100000, seconds: 1.166, > num_per_second: 85763.3 > times-optimized-normal.txt : num: 100000, seconds: 1.242, > num_per_second: 80515.3 > times-optimized-modified.txt: num: 100000, seconds: 0.825, > num_per_second: 121212 > : Serializing Heartbeat messages to > strings: > times-debug-normal.txt : num: 100000, seconds: 2.35, > num_per_second: 42553.2 > times-debug-modified.txt : num: 100000, seconds: 1.198, > num_per_second: 83472.5 > times-optimized-normal.txt : num: 100000, seconds: 1.425, > num_per_second: 70175.4 > times-optimized-modified.txt: num: 100000, seconds: 0.707, > num_per_second: 141443 > : Serializing Heartbeat messages from > strings: > times-debug-normal.txt : num: 100000, seconds: 4.749, > num_per_second: 21057.1 > times-debug-modified.txt : num: 100000, seconds: 2.671, > num_per_second: 37439.2 > times-optimized-normal.txt : num: 100000, seconds: 3.053, > num_per_second: 32754.7 > times-optimized-modified.txt: num: 100000, seconds: 1.782, > num_per_second: 56116.7 > : Creating NewOrderSingle messages: > times-debug-normal.txt : num: 100000, seconds: 6.422, > num_per_second: 15571.5 > times-debug-modified.txt : num: 100000, seconds: 3.937, > num_per_second: 25400.1 > times-optimized-normal.txt : num: 100000, seconds: 4.778, > num_per_second: 20929.3 > times-optimized-modified.txt: num: 100000, seconds: 3.026, > num_per_second: 33046.9 > : Serializing NewOrderSingle messages to > strings: > times-debug-normal.txt : num: 100000, seconds: 6.429, > num_per_second: 15554.5 > times-debug-modified.txt : num: 100000, seconds: 3.84, > num_per_second: 26041.7 > times-optimized-normal.txt : num: 100000, seconds: 4.874, > num_per_second: 20517 > times-optimized-modified.txt: num: 100000, seconds: 3.029, > num_per_second: 33014.2 > : Serializing NewOrderSingle messages from > strings: > times-debug-normal.txt : num: 100000, seconds: 9.608, > num_per_second: 10408 > times-debug-modified.txt : num: 100000, seconds: 5.385, > num_per_second: 18570.1 > times-optimized-normal.txt : num: 100000, seconds: 7.202, > num_per_second: 13885 > times-optimized-modified.txt: num: 100000, seconds: 3.779, > num_per_second: 26462 > : Creating QuoteRequest messages: > times-debug-normal.txt : num: 100000, seconds: 93.295, > num_per_second: 1071.87 > times-debug-modified.txt : num: 100000, seconds: 46.975, > num_per_second: 2128.79 > times-optimized-normal.txt : num: 100000, seconds: 61.148, > num_per_second: 1635.38 > times-optimized-modified.txt: num: 100000, seconds: 33.904, > num_per_second: 2949.5 > : Serializing QuoteRequest messages to > strings: > times-debug-normal.txt : num: 100000, seconds: 11.052, > num_per_second: 9048.14 > times-debug-modified.txt : num: 100000, seconds: 10.691, > num_per_second: 9353.66 > times-optimized-normal.txt : num: 100000, seconds: 5.667, > num_per_second: 17646 > times-optimized-modified.txt: num: 100000, seconds: 4.808, > num_per_second: 20798.7 > : Serializing QuoteRequest messages from > strings: > times-debug-normal.txt : num: 100000, seconds: 74.91, > num_per_second: 1334.94 > times-debug-modified.txt : num: 100000, seconds: 48.556, > num_per_second: 2059.48 > times-optimized-normal.txt : num: 100000, seconds: 47.944, > num_per_second: 2085.77 > times-optimized-modified.txt: num: 100000, seconds: 28.627, > num_per_second: 3493.21 > : Reading fields from QuoteRequest message: > times-debug-normal.txt : num: 100000, seconds: 34.526, > num_per_second: 2896.37 > times-debug-modified.txt : num: 100000, seconds: 12.988, > num_per_second: 7699.41 > times-optimized-normal.txt : num: 100000, seconds: 21.891, > num_per_second: 4568.09 > times-optimized-modified.txt: num: 100000, seconds: 6.875, > num_per_second: 14545.5 > : Storing NewOrderSingle messages: > times-debug-normal.txt : num: 100000, seconds: 0.765, > num_per_second: 130719 > times-debug-modified.txt : num: 100000, seconds: 0.71, > num_per_second: 140845 > times-optimized-normal.txt : num: 100000, seconds: 0.575, > num_per_second: 173913 > times-optimized-modified.txt: num: 100000, seconds: 0.521, > num_per_second: 191939 > : Validating NewOrderSingle messages with > no data dictionary: > times-debug-normal.txt : num: 100000, seconds: 1.489, > num_per_second: 67159.2 > times-debug-modified.txt : num: 100000, seconds: 0.859, > num_per_second: 116414 > times-optimized-normal.txt : num: 100000, seconds: 0.843, > num_per_second: 118624 > times-optimized-modified.txt: num: 100000, seconds: 0.426, > num_per_second: 234742 > : Validating NewOrderSingle messages with > data dictionary: > times-debug-normal.txt : num: 100000, seconds: 7.732, > num_per_second: 12933.3 > times-debug-modified.txt : num: 100000, seconds: 6.105, > num_per_second: 16380 > times-optimized-normal.txt : num: 100000, seconds: 2.485, > num_per_second: 40241.4 > times-optimized-modified.txt: num: 100000, seconds: 1.717, > num_per_second: 58241.1 > > > --- quickfix/src/C++/FieldConvertors.h 2004-04-29 20:58:20.000000000 > +0100 > +++ quickfix-debug-modified/src/C++/FieldConvertors.h 2004-08-12 > 19:13:22.036829060 +0100 > @@ -1,3 +1,4 @@ > + > /* -*- C++ -*- */ > > > / > *********************************************************************** > ***** > @@ -32,6 +33,76 @@ > > namespace FIX > { > +template<class T> > +static inline char * integer_to_string (char * buf, const size_t len, > T t) > +{ > + const bool is_neg = t < 0; > + char * p = buf + len; > + > + *--p = '\0'; > + > + if (is_neg) { > + if (t == std::numeric_limits<T>::min()) { > + *--p = '0' + (10-t%10)%10; > + t/=10; > + } > + t=-t; > + do { > + *--p = '0' + t % 10; > + t /= 10; > + } while (t > 0); > + *--p = '-'; > + } else { > + do { > + *--p = '0' + t % 10; > + t /= 10; > + } while (t > 0); > + } > + return p; > +} > + > + > + > +template<class T> > +static inline char * integer_to_string_padded (char * buf, > + const size_t len, > + T t, > + const size_t width = 0, > + const char padding_char = '0') > +{ > + if (!width) return integer_to_string (buf, len, t); > + > + const bool is_neg = t < 0; > + char * p = buf + len; > + > + *--p = '\0'; > + > + if (is_neg) { > + if (t == std::numeric_limits<T>::min()) { > + *--p = '0' + (10-t%10)%10; > + t/=10; > + } > + t=-t; > + do { > + *--p = '0' + t % 10; > + t /= 10; > + } while (t > 0); > + if (p > buf) > + *--p = '-'; > + } else { > + do { > + *--p = '0' + t % 10; > + t /= 10; > + } while (t > 0); > + } > + const char * stop_p = buf + len - width - 1; > + if (stop_p < buf) stop_p = buf; > + while (p > stop_p) > + *--p = padding_char; > + return p; > +} > + > + > /// Empty convertor is a no-op. > struct EmptyConvertor > { > @@ -46,21 +117,32 @@ > { > static std::string convert( long value ) > { > - char temp[ 12 ]; > - memset( temp, 0, 12 * sizeof( char ) ); > - sprintf( temp, "%d", ( int ) value ); > - return temp; > + // buffer is big enough for significant digits and extra digit, > minus and null > + char buffer [std::numeric_limits<long>::digits10 + 3]; > + const char * const start = integer_to_string (buffer, sizeof > (buffer), value); > + return std::string (start, buffer + sizeof (buffer) - start - 1); > } > > static bool convert( const std::string& value, long& result ) > { > const char * str = value.c_str(); > - if ( *str == '-' ) ++str; > - for ( const char * p = str; *p != 0; ++p ) > - { > - if ( !isdigit( *p ) ) return false; > + bool is_neg = false; > + long x = 0; > + > + if (*str == '-') { > + is_neg = true; > + ++str; > } > - result = atol( value.c_str() ); > + > + do { > + const int c = *str - '0'; > + if (c < 0 || 9 < c) return false; > + x = 10 * x + c; > + } while (*++str); > + > + if (is_neg) x = -x; > + > + result = x; > return true; > } > > @@ -81,9 +163,10 @@ > throw( FieldConvertError& ) > { > if ( value > 255 || value < 0 ) throw FieldConvertError(); > - std::stringstream stream; > - stream << std::setw( 3 ) << std::setfill( '0' ) << value; > - return stream.str(); > + char result [4]; > + if (integer_to_string_padded (result, sizeof(result), value, 3, > '0') != result) > + throw FieldConvertError(); > + return std::string (result, 3); > } > > static bool convert( const std::string& value, long& result ) > @@ -104,53 +187,32 @@ > static std::string convert( double value ) > { > char result[ 32 ]; > - memset( result, 0, 32 * sizeof( char ) ); > - sprintf( result, "%.15g", value ); > - return result; > + return std::string (result, std::sprintf( result, "%.15g", value > )); > } > > static bool convert( const std::string& value, double& result ) > { > - if ( !value.size() ) return false; > - if ( value == "." ) return false; > - > - result = atof( const_cast < char* > ( value.c_str() ) ); > - std::string stripped = value; > - > - // add leading zero if none exists > - if ( *stripped.begin() == '.' ) > - stripped = '0' + stripped; > - > - // remove extra leading zeros > - while ( stripped.size() > 1 > - && *(stripped.begin()) == '0' > - && isdigit(*(stripped.begin()+1)) ) > - { > - stripped.erase( stripped.begin() ); > + const char * i = value.c_str(); > + > + if (!*i) return false; // Catch null > strings > + if (*i == '-' && !*++i) return false; // Eat leading > '-' and recheck for null string > + > + bool have_digit = false; > + > + if (std::isdigit (*i)) { > + have_digit = true; > + while (std::isdigit (*++i)); > } > > - if ( stripped.find( '.' ) != std::string::npos ) > - { > - std::string::reverse_iterator r = stripped.rbegin(); > - while ( r != stripped.rend() && ( *r == '0' || *r == '.' ) ) > - { > - if ( *r == '.' ) { stripped.resize( stripped.size() - 1 ); > break; } > - stripped.resize( stripped.size() - 1 ); r++; > - } > - } > - if ( *stripped.begin() == '0' ) > - { > - std::string::size_type i = stripped.find_first_not_of( '0' ); > - std::string::size_type dot = stripped.find( '.' ); > - if ( i != dot ) > - stripped = stripped.substr( i ); > + if (*i == '.' && std::isdigit (*++i)) { > + have_digit = true; > + while (std::isdigit (*++i)); > } > > - std::string converted = convert( result ); > - if ( stripped.size() != converted.size() ) return false; > + if (*i || !have_digit) return false; > > - // strcmp is being used because == operator is funky under linux > - return ( strcmp( stripped.c_str(), converted.c_str() ) == 0 ); > + result = std::strtod (value.c_str(), 0); > + return true; > } > > static double convert( const std::string& value ) > @@ -169,16 +231,13 @@ > { > static std::string convert( char value ) > { > - char temp[ 2 ]; > - temp[ 0 ] = value; > - temp[ 1 ] = '\0'; > - return temp; > + return std::string (1, value); > } > > static bool convert( const std::string& value, char& result ) > { > - if ( value.size() == 0 ) return false; > - result = *value.c_str(); > + if ( value.size() != 1 ) return false; > + result = value[0]; > return true; > } > > @@ -197,15 +256,19 @@ > struct BoolConvertor > { > static std::string convert( bool value ) > - { return value ? "Y" : "N"; } > + { > + const char ch = value ? 'Y' : 'N'; > + return std::string (1, ch); > + } > > static bool convert( const std::string& value, bool& result ) > { > - if ( value == "Y" ) result = true; > - else > - if ( value == "N" ) result = false; > - else > - return false; > + if (value.size() != 1) return false; > + switch (value[0]) { > + case 'Y': result = true; break; > + case 'N': result = false; break; > + default: return false; > + } > return true; > } > > @@ -227,13 +290,19 @@ > throw( FieldConvertError& ) > { > char result[ 18+4 ]; > - int len = strftime( result, 18, "%Y%m%d-%H:%M:%S", value ); > - if ( len != 17 ) throw FieldConvertError(); > + > + integer_to_string_padded (result , 5, static_cast<const > tm*>(value)->tm_year + 1900, 4, '0'); > + integer_to_string_padded (result + 4, 3, static_cast<const > tm*>(value)->tm_mon + 1, 2, '0'); > + integer_to_string_padded (result + 6, 3, static_cast<const > tm*>(value)->tm_mday, 2, '0'); result[8] = '-'; > + integer_to_string_padded (result + 9, 3, static_cast<const > tm*>(value)->tm_hour, 2, '0'); result[11] = ':'; > + integer_to_string_padded (result + 12, 3, static_cast<const > tm*>(value)->tm_min, 2, '0'); result[14] = ':'; > + integer_to_string_padded (result + 15, 3, static_cast<const > tm*>(value)->tm_sec, 2, '0'); > > if(true==showMilliseconds) > { > - len = sprintf(result+17,".%03d",value.getMillisecond()); > - if ( len != 4) throw FieldConvertError(); > + result[17] = '.'; > + if (integer_to_string_padded (result + 18, 4, > value.getMillisecond(), 3, '0') != result + 18) > + throw FieldConvertError(); > } > return result; > } > @@ -242,19 +311,73 @@ > throw( FieldConvertError& ) > { > UtcTimeStamp result; > - const char* val = value.c_str(); > - const char* len = strptime( val, "%Y%m%d-%H:%M:%S", result ); > - if ( len - val != 17 ) throw FieldConvertError(); > - > - // if we have milliseconds in the string, *len should be ".sss" > - result.setMillisecond(0); > - if(NULL != len && strlen(len)==4 && len[0] == '.') > - { > - int ms = atoi(&len[1]); > - if(ms < 0 || ms > 999) throw FieldConvertError(); > - result.setMillisecond(ms); > + bool have_milliseconds = false; > + > + switch (value.size()) { > + case 21: have_milliseconds = true; > + case 17: break; > + default: throw FieldConvertError(); > } > > + int i = 0; > + for (int c=0; c<8; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (value[i++] != '-') throw > FieldConvertError(); > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (value[i++] != ':') throw > FieldConvertError(); > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (value[i++] != ':') throw > FieldConvertError(); > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (have_milliseconds) { > + if (value[i++] != '.') throw FieldConvertError(); > + for (int c=0; c<3; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + } > + > + tm & result_tm = *static_cast<tm*>(result); > + > + i = 0; > + > + result_tm.tm_year = value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year -= 1900; > + > + result_tm.tm_mon = value[i++] - '0'; > + result_tm.tm_mon = 10 * result_tm.tm_mon + value[i++] - '0'; > + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw > FieldConvertError(); > + --result_tm.tm_mon; > + > + result_tm.tm_mday = value[i++] - '0'; > + result_tm.tm_mday = 10 * result_tm.tm_mday + value[i++] - '0'; > + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw > FieldConvertError(); > + > + ++i; // skip '-' > + > + result_tm.tm_hour = value[i++] - '0'; > + result_tm.tm_hour = 10 * result_tm.tm_hour + value[i++] - '0'; > + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No > check for >= 0 as no '-' are converted here > + > + ++i; // skip ':' > + > + result_tm.tm_min = value[i++] - '0'; > + result_tm.tm_min = 10 * result_tm.tm_min + value[i++] - '0'; > + if (59 < result_tm.tm_min) throw FieldConvertError(); // No check > for >= 0 as no '-' are converted here > + > + ++i; // skip ':' > + > + result_tm.tm_sec = value[i++] - '0'; > + result_tm.tm_sec = 10 * result_tm.tm_sec + value[i++] - '0'; > + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No check > for >= 0 as no '-' are converted here > + > + if (have_milliseconds) > + result.setMillisecond (100 * (value[i+1] - '0') + > + 10 * (value[i+2] - '0') + > + (value[i+3] - '0')); > + else > + result.setMillisecond (0); > + > + result_tm.tm_isdst = -1; > + > return result; > } > }; > @@ -266,13 +389,16 @@ > throw( FieldConvertError& ) > { > char result[ 9+4 ]; > - int len = strftime( result, 9, "%H:%M:%S", value ); > - if ( len != 8 ) throw FieldConvertError(); > + > + integer_to_string_padded (result , 3, static_cast<const > tm*>(value)->tm_hour, 2, '0'); result[2] = ':'; > + integer_to_string_padded (result + 3, 3, static_cast<const > tm*>(value)->tm_min, 2, '0'); result[5] = ':'; > + integer_to_string_padded (result + 6, 3, static_cast<const > tm*>(value)->tm_sec, 2, '0'); > > if(true==showMilliseconds) > { > - len = sprintf(result+8,".%03d",value.getMillisecond()); > - if ( len != 4) throw FieldConvertError(); > + result[8] = '.'; > + if (integer_to_string_padded (result + 9, 4, > value.getMillisecond(), 3, '0') != result + 9) > + throw FieldConvertError(); > } > > return result; > @@ -282,19 +408,52 @@ > throw( FieldConvertError& ) > { > UtcTimeOnly result; > - const char* val = value.c_str(); > - const char* len = strptime( val, "%H:%M:%S", result ); > - if ( len - val != 8 ) throw FieldConvertError(); > - > - // if we have milliseconds in the string, *len should be ".sss" > - result.setMillisecond(0); > - if(NULL != len && strlen(len)==4 && len[0] == '.') > - { > - int ms = atoi(&len[1]); > - if(ms < 0 || ms > 999) throw FieldConvertError(); > - result.setMillisecond(ms); > + bool have_milliseconds = false; > + > + switch (value.size()) { > + case 12: have_milliseconds = true; > + case 8: break; > + default: throw FieldConvertError(); > + } > + > + int i = 0; > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (value[i++] != ':') throw > FieldConvertError(); > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (value[i++] != ':') throw > FieldConvertError(); > + for (int c=0; c<2; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + if (have_milliseconds) { > + // ++i instead of i++ skips the '.' separator > + for (int c=0; c<3; ++c) if (!std::isdigit(value[++i])) throw > FieldConvertError(); > } > > + tm & result_tm = *static_cast<tm*>(result); > + > + i = 0; > + > + result_tm.tm_hour = value[i++] - '0'; > + result_tm.tm_hour = 10 * result_tm.tm_hour + value[i++] - '0'; > + if (23 < result_tm.tm_hour) throw FieldConvertError(); // No > check for >= 0 as no '-' are converted here > + ++i; // skip ':' > + > + result_tm.tm_min = value[i++] - '0'; > + result_tm.tm_min = 10 * result_tm.tm_min + value[i++] - '0'; > + if (59 < result_tm.tm_min) throw FieldConvertError(); // No check > for >= 0 as no '-' are converted here > + ++i; // skip ':' > + > + result_tm.tm_sec = value[i++] - '0'; > + result_tm.tm_sec = 10 * result_tm.tm_sec + value[i++] - '0'; > + if (60 < result_tm.tm_sec) throw FieldConvertError(); // No check > for >= 0 as no '-' are converted here > + > + if (have_milliseconds) > + result.setMillisecond (100 * (value[i+1] - '0') + > + 10 * (value[i+2] - '0') + > + (value[i+3] - '0')); > + else > + result.setMillisecond (0); > + > + result_tm.tm_isdst = -1; > + > return result; > } > }; > @@ -306,8 +465,9 @@ > throw( FieldConvertError& ) > { > char result[ 9 ]; > - int len = strftime( result, 9, "%Y%m%d", value ); > - if ( len != 8 ) throw FieldConvertError(); > + integer_to_string_padded (result , 5, static_cast<const > tm*>(value)->tm_year + 1900, 4, '0'); > + integer_to_string_padded (result + 4, 3, static_cast<const > tm*>(value)->tm_mon + 1, 2, '0'); > + integer_to_string_padded (result + 6, 3, static_cast<const > tm*>(value)->tm_mday, 2, '0'); > return result; > } > > @@ -315,9 +475,34 @@ > throw( FieldConvertError& ) > { > UtcDate result; > - const char* val = value.c_str(); > - const char* len = strptime( val, "%Y%m%d", result ); > - if ( len - val != 8 ) throw FieldConvertError(); > + > + if (value.size() != 8) throw FieldConvertError(); > + > + int i = 0; > + for (int c=0; c<8; ++c) if (!std::isdigit(value[i++])) throw > FieldConvertError(); > + > + tm & result_tm = *static_cast<tm*>(result); > + > + i = 0; > + > + result_tm.tm_year = value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year = 10 * result_tm.tm_year + value[i++] - '0'; > + result_tm.tm_year -= 1900; > + > + result_tm.tm_mon = value[i++] - '0'; > + result_tm.tm_mon = 10 * result_tm.tm_mon + value[i++] - '0'; > + if (result_tm.tm_mon < 1 || 12 < result_tm.tm_mon) throw > FieldConvertError(); > + --result_tm.tm_mon; > + > + result_tm.tm_mday = value[i++] - '0'; > + result_tm.tm_mday = 10 * result_tm.tm_mday + value[i++] - '0'; > + if (result_tm.tm_mday < 1 || 31 < result_tm.tm_mday) throw > FieldConvertError(); > + ++i; // skip '-' > + > + result_tm.tm_isdst = -1; > + > return result; > } > }; > > > <font face="Times New Roman" size="3"> > <p>-------------------------------------------------------------------- > ----------</p> > <p> This email is intended only for the use of the individual(s) to > whom it is addressed and may be privileged and confidential. > Unauthorised use or disclosure is prohibited. If you receive this > e-mail in error, please advise immediately and delete the original > message. This message may have been altered without your or our > knowledge and the sender does not accept any liability for any errors > or omissions in the message.</p> > <p>====================================================</p> > </font> > > > > ------------------------------------------------------- > SF.Net email is sponsored by Shop4tech.com-Lowest price on Blank Media > 100pk Sonic DVD-R 4x for only $29 -100pk Sonic DVD+R for only $33 > Save 50% off Retail on Ink & Toner - Free Shipping and Free Gift. > http://www.shop4tech.com/z/Inkjet_Cartridges/9_108_r285 > _______________________________________________ > Quickfix-developers mailing list > Qui...@li... > https://lists.sourceforge.net/lists/listinfo/quickfix-developers > |