diff --git a/doc/performance.qbk b/doc/performance.qbk index e4d2c15..4e48075 100644 --- a/doc/performance.qbk +++ b/doc/performance.qbk @@ -5,28 +5,28 @@ The performance of ['Boost.Convert] depends entirely on the converter deployed. Performance was tested for string conversions to the `int` type and to a user-defined type. On a 12.04 Ubuntu box (compiled with gcc 4.6.3) tests produced the following results: g++ -O0 test_convert.cpp <--- NON-OPTIMIZED - str-to-int: strtol/scanf/lcast/sstream=0.49/1.40/1.56/2.00 seconds. + str-to-int: strtol/scanf/lcast/sstream=0.49/1.40/1.56/1.77 seconds. int-to-str: ltostr/prntf/lcast/sstream=3.10/2.62/2.42/2.52 seconds. str-to-user-type: lcast/sstream=4.94/3.19 seconds. g++ -O3 test_convert.cpp <--- OPTIMIZED - str-to-int: strtol/scanf/lcast/sstream=0.28/1.15/0.45/1.70 seconds. + str-to-int: strtol/scanf/lcast/sstream=0.28/1.15/0.45/1.23 seconds. int-to-str: ltostr/prntf/lcast/sstream=1.50/2.17/1.11/1.73 seconds. - str-to-user-type: lcast/sstream=3.26/2.58 seconds. - user-type-to-str: lcast/sstream=5.22/1.30 seconds. + str-to-user-type: lcast/sstream=3.58/1.99 seconds. + user-type-to-str: lcast/sstream=6.22/1.32 seconds. [note Non-optimized times are more of a curiosity -- to demonstrate (and to remind) that a noticeable performance improvement is to be gained via optimized compilation. Optimized times are the ones to be looked at and analyzed.] On the Cygwin platform compiled with gcc 4.8.2: g++ -O3 test_convert.cpp - str-to-int: strtol/scanf/lcast/sstream=0.28/1.20/1.22/2.42 seconds. + str-to-int: strtol/scanf/lcast/sstream=0.28/1.20/1.22/1.82 seconds. int-to-str: ltostr/prntf/lcast/sstream=1.31/2.94/2.62/2.03 seconds. str-to-user-type: lcast/sstream=7.44/4.61 seconds. Compiled with MS Visual C++ 2010 Express optimized for speed: - str-to-int: strtol/scanf/lcast/sstream=0.28/0.89/1.03/6.20 seconds. + str-to-int: strtol/scanf/lcast/sstream=0.28/0.89/1.03/4.65 seconds. int-to-str: ltostr/prntf/lcast/sstream=1.11/1.86/1.91/5.92 seconds. str-to-user-type: lcast/sstream=6.81/3.30 seconds. diff --git a/include/boost/convert/converter/sstream.hpp b/include/boost/convert/converter/sstream.hpp index acf77af..32636ac 100644 --- a/include/boost/convert/converter/sstream.hpp +++ b/include/boost/convert/converter/sstream.hpp @@ -24,7 +24,9 @@ struct boost::basic_stringstream_converter typedef Char char_type; typedef basic_stringstream_converter this_type; typedef std::basic_stringstream stream_type; - typedef detail::parser_buf, char_type> buffer_type; + typedef std::basic_istream istream_type; + typedef std::basic_streambuf buffer_type; + typedef detail::parser_buf parser_type; typedef std::basic_string string_type; typedef std::ios_base& (*manipulator_type)(std::ios_base&); @@ -48,15 +50,29 @@ struct boost::basic_stringstream_converter bool>::type operator()(StringIn const& string_in, TypeOut& result_out) const { - // A lot of optimization is to be done here. - // See shr_using_base_class(InputStreamable& output) in lexical_cast.hpp + typedef conversion::string_range str_range; + + istream_type& istream = stream_; + parser_type strbuf; + buffer_type* oldbuf = istream.rdbuf(); + char_type const* beg = str_range::begin(string_in); + std::streamsize sz = str_range::size(string_in); stream_.clear(); // Clear the flags - stream_.str(string_in); // Set the content of the stream +// stream_.str(string_in); // Copy the content to the internal buffer +// stream_ >> result_out; - stream_ >> result_out; + // The code below (pretty much stolen from shr_using_base_class(InputStreamable& output) in lexical_cast.hpp + // uses the provided string_in as the buffer and, consequently, avoids the overhead associated with + // stream_.str(string_in) -- copying of the content into internal buffer. - return !stream_.fail() && stream_.eof(); + strbuf.setbuf(const_cast(beg), sz); + istream.rdbuf(&strbuf); + istream >> result_out; + bool result = !istream.fail() && istream.eof(); + istream.rdbuf(oldbuf); + + return result; } this_type& operator() (std::locale const& locale) { return (stream_.imbue(locale), *this); } diff --git a/include/boost/convert/detail/string_sfinae.hpp b/include/boost/convert/detail/string_sfinae.hpp index fe8ff6f..2c52821 100644 --- a/include/boost/convert/detail/string_sfinae.hpp +++ b/include/boost/convert/detail/string_sfinae.hpp @@ -111,24 +111,35 @@ struct string_range }; template -struct string_range >::type> +struct string_range >::type> { typedef typename boost::range_value::type char_type; - typedef char_type* iterator; + typedef char_type const* iterator; static iterator begin (String const& s) { return s; } - static iterator end (String const& s) { return s + strlen(s); } +// static iterator end (String const& s) { return s + strlen(s); } static std::streamsize size (String const& s) { return std::streamsize(strlen(s)); } }; +template +struct string_range >::type> +{ + typedef typename boost::range_value::type char_type; + typedef char_type const* iterator; + + static iterator begin (String const& s) { return s; } +// static iterator end (String const& s) { return s + strlen(s); } + static std::streamsize size (String const& s) { return std::streamsize(wcslen(s)); } +}; + template struct string_range >::type> { typedef typename boost::range_value::type char_type; - typedef typename String::iterator iterator; + typedef char_type const* iterator; - static iterator begin (String const& s) { return s.begin(); } - static iterator end (String const& s) { return s.end(); } + static iterator begin (String const& s) { return &*s.begin(); } +// static iterator end (String const& s) { return s.end(); } static std::streamsize size (String const& s) { return std::streamsize(s.size()); } }; diff --git a/test/int_to_str.cpp b/test/int_to_str.cpp index 4187265..8e33e65 100644 --- a/test/int_to_str.cpp +++ b/test/int_to_str.cpp @@ -57,34 +57,4 @@ test::int_to_string() BOOST_TEST(rs11 && rs11.value() == "-1"); BOOST_TEST(rs12 && rs12.value() == L"-2"); - //////////////////////////////////////////////////////////////////////////// - // Testing conversion::result<> interface. - // conversion::result exhibits the SAME (but delayed) behavior, i.e. - // for failed conversion it throws on an attempt to retrieve the value - // as there is nothing to return. - //////////////////////////////////////////////////////////////////////////// - - cnv::result const r010 = boost::convert(not_int_str, ccnv); - cnv::result const r011 = boost::convert(std_str, ccnv); - cnv::result const r012 = boost::convert(c_str, ccnv); - cnv::result const r013 = boost::convert(wstd_str, wcnv); - cnv::result const r014 = boost::convert(wc_str, wcnv); - cnv::result const r015 = boost::convert(array_str, ccnv); - - BOOST_TEST(!r010); // Failed conversion - BOOST_TEST( r011 && r011.value() == -11); - BOOST_TEST( r012 && r012.value() == -12); - BOOST_TEST( r013 && r013.value() == -13); - BOOST_TEST( r014 && r014.value() == -14); - BOOST_TEST( r015 && r015.value() == -15); - - try - { - r010.value(); // Throws on an attempt to retrieve the value. - BOOST_TEST(!"failed to throw"); - } - catch (std::exception&) - { - // Expected behavior: received 'boost::convert failed' exception. All well. - } } diff --git a/test/str_to_int.cpp b/test/str_to_int.cpp index 410b10c..7ea22db 100644 --- a/test/str_to_int.cpp +++ b/test/str_to_int.cpp @@ -31,19 +31,19 @@ test::string_to_int() // On failure returns the provided fallback value and DOES NOT THROW. //////////////////////////////////////////////////////////////////////////// - int const a000 = boost::convert(not_int_str, ccnv).value_or(-1); - int const a001 = boost::convert(std_str, ccnv).value_or(-1); - int const a002 = boost::convert(c_str, ccnv).value_or(-1); - int const a003 = boost::convert(wstd_str, wcnv).value_or(-1); - int const a004 = boost::convert(wc_str, wcnv).value_or(-1); - int const a005 = boost::convert(array_str, ccnv).value_or(-1); + int const a00 = boost::convert(not_int_str, ccnv).value_or(-1); + int const a01 = boost::convert(std_str, ccnv).value_or(-1); + int const a02 = boost::convert(c_str, ccnv).value_or(-1); + int const a03 = boost::convert(wstd_str, wcnv).value_or(-1); + int const a04 = boost::convert(wc_str, wcnv).value_or(-1); + int const a05 = boost::convert(array_str, ccnv).value_or(-1); - BOOST_TEST(a000 == -1); // Failed conversion - BOOST_TEST(a001 == -11); - BOOST_TEST(a002 == -12); - BOOST_TEST(a003 == -13); - BOOST_TEST(a004 == -14); - BOOST_TEST(a005 == -15); + BOOST_TEST(a00 == -1); // Failed conversion + BOOST_TEST(a01 == -11); + BOOST_TEST(a02 == -12); + BOOST_TEST(a03 == -13); + BOOST_TEST(a04 == -14); + BOOST_TEST(a05 == -15); //////////////////////////////////////////////////////////////////////////// // Testing with the fallback value value provided. @@ -92,4 +92,35 @@ test::string_to_int() BOOST_TEST(a023 == -13); BOOST_TEST(a024 == -14); BOOST_TEST(a025 == -15); + + //////////////////////////////////////////////////////////////////////////// + // Testing conversion::result<> interface. + // conversion::result exhibits the SAME (but delayed) behavior, i.e. + // for failed conversion it throws on an attempt to retrieve the value + // as there is nothing to return. + //////////////////////////////////////////////////////////////////////////// + + cnv::result const r010 = boost::convert(not_int_str, ccnv); + cnv::result const r011 = boost::convert(std_str, ccnv); + cnv::result const r012 = boost::convert(c_str, ccnv); + cnv::result const r013 = boost::convert(wstd_str, wcnv); + cnv::result const r014 = boost::convert(wc_str, wcnv); + cnv::result const r015 = boost::convert(array_str, ccnv); + + BOOST_TEST(!r010); // Failed conversion + BOOST_TEST( r011 && r011.value() == -11); + BOOST_TEST( r012 && r012.value() == -12); + BOOST_TEST( r013 && r013.value() == -13); + BOOST_TEST( r014 && r014.value() == -14); + BOOST_TEST( r015 && r015.value() == -15); + + try + { + r010.value(); // Throws on an attempt to retrieve the value. + BOOST_TEST(!"failed to throw"); + } + catch (std::exception&) + { + // Expected behavior: received 'boost::convert failed' exception. All well. + } }