convert/doc/performance.qbk
2018-01-11 11:44:30 +11:00

106 lines
8.0 KiB
Plaintext

[/
Copyright (c) Vladimir Batov 2009-2016
Distributed under the Boost Software License, Version 1.0.
See copy at http://www.boost.org/LICENSE_1_0.txt.
]
[section:performance Performance]
[section:converters_compared Converters Compared]
The performance of ['Boost.Convert] depends entirely on the performance of the converter deployed. A few converters have been tested for string conversions to basic types and to a user-defined type.
In turn, the performance of every particular converter depends on the platform, the compiler used and the particular implementation of the underlying conversion component (`std::stream`, `printf`, ['Boost.Spirit], etc.). Consequently, the results below are only an approximate indication of ['relative] performance of the mentioned converters on the tested platforms.
When compiled with gcc-5.4.0 on 64-bit Ubuntu 16.04, tests produced the following results:
str-to-int: spirit/strtol/lcast/scanf/stream= 0.27/ 0.35/ 0.92/ 2.11/ 2.09 seconds.
str-to-lng: spirit/strtol/lcast/scanf/stream= 0.69/ 0.31/ 1.28/ 2.07/ 2.50 seconds.
str-to-dbl: spirit/strtol/lcast/scanf/stream= 0.73/ 1.06/ 7.95/ 2.87/ 5.10 seconds.
int-to-str: spirit/strtol/lcast/prntf/stream= 1.96/ 1.39/ 2.52/ 3.49/ 2.58 seconds.
lng-to-str: spirit/strtol/lcast/prntf/stream= 2.45/ 1.51/ 2.32/ 3.30/ 2.63 seconds.
dbl-to-str: spirit/strtol/lcast/prntf/stream= 6.62/ 4.46/ 28.69/ 20.60/ 14.16 seconds.
Based on the results, all things considered, I tend to conclude that there is no clear winner:
* the ['Spirit.Qi]-based converter was the fastest for string to basic (`int`, `double`) conversions. So, it might be a good candidate for the tasks predominantly doing that kind of conversions (with ['Spirit.Qi] conversion-related limitations in mind); ['Spirit.Karma]'s ['to-string] performance did not seem as impressive;
* the `std::iostream`-based converter was comparatively slow. Still, given its maturity, availability and formatting support, it might be an option to consider if conversion performance is not your primary concern;
* the `strtol`-inspired converter was reasonably fast and with formatting support might be an attractive all-rounder. It should be noted that it is nowhere as mature as `boost::cnv::lexical_cast` or `boost::cnv::stream`. So, bugs are to be expected.
For user-defined types `cnv::lexical_cast`, `cnv::cstream` and `cnv::strtol` were tested with the following results:
str-to-user-type: lcast/stream/strtol=0.36/0.18/0.07 seconds.
user-type-to-str: lcast/stream/strtol=0.58/0.09/0.06 seconds.
To provide ['string-to-user-type] and ['user-type-to-string] conversions the first two deploy the same standard `std::iostream` library. However, `boost::cnv::cstream` considerably outperforms `boost::lexical_cast` in these tests. The results reflect different underlying designs. Namely,
the standard ['Boost.Convert] deployment pattern is to create a converter or converters once and then re-use them. `boost::lexical_cast`, on the other hand, creates and then destroys a `std::stream` instance every time the function is called and the [@http://www.boost.org/doc/libs/1_55_0/doc/html/boost_lexical_cast/performance.html `boost::lexical_cast` performance] table indicates that the "std::stringstream ['with construction]" operation is considerably more expensive compared to "std::stringstream ['without construction]".
`boost::cnv::strtol` support for user types has been implemented similarly but without the `std::stream`-related overhead. That resulted in the best out-of-three performance results.
Based on the performance data, I tend to conclude that, given type-safety and benefits provided by the ['Boost.Convert] framework, it (with appropriate converters) should probably be the first choice for conversion-related tasks.
[endsect]
[section Boost.Convert Overhead]
['Boost.Convert] framework adds an additional layer of indirection and some ['Boost.Convert] converters are wrappers around actual conversion facilities such as `boost::lexical_cast`, `boost::spirit`, etc. Consequently, there might be reasonable concerns with regard to the performance overhead introduced by the framework as opposed to deploying conversion facilities directly.
To test that code has been borrowed and adapted from the Spirit.Qi performance/optimization framework (see $BOOST_ROOT/libs/spirit/workbench/qi/int_parser.cpp). The tests were
# compiled using gcc-4.6.3 and gcc-4.8.2;
# with optimization: `g++ -O3 test/performance_spirit.cpp -Iinclude -I../boost_1_56_0 -lrt`;
# on 64-bit Ubuntu 12.04 and 32-bit Ubuntu 14.04;
# run against the input of randomly generated 18 numeric strings (9 positive and 9 negative numbers with the number of digits from 1 to 9);
# run for
* `boost::lexical_cast` and `boost::lexical_cast`-based converter;
* `boost::spirit::qi::parse` and `boost::spirit::qi::parse`-based converter.
The purpose of the test was to deploy the same functionality directly and as part of the ['Boost.Convert] framework. Results are shown below for several consecutive runs:
gcc-4.6.3 (64-bit)
raw_lxcast_str_to_int_test: 1.0504170070 [s]
cnv_lxcast_str_to_int_test: 1.0610595810 [s] (1% slower than raw above)
raw_spirit_str_to_int_test: 0.2845369110 [s]
cnv_spirit_str_to_int_test: 0.2834834560 [s] (1% faster than raw above)
raw_lxcast_str_to_int_test: 1.0770350390 [s] (2% slower than prev. run)
cnv_lxcast_str_to_int_test: 1.0607665160 [s] (1% faster than raw above)
raw_spirit_str_to_int_test: 0.2792295470 [s] (2% faster than prev. run)
cnv_spirit_str_to_int_test: 0.2827574570 [s] (1% slower than raw above)
gcc-4.8.2 (32-bit)
raw_lxcast_str_to_int_test: 8.5153330600 [s]
cnv_lxcast_str_to_int_test: 8.6989499850 [s] (2% slower than raw above)
raw_spirit_str_to_int_test: 2.4197476510 [s]
cnv_spirit_str_to_int_test: 2.4594171510 [s] (2% slower than raw above)
raw_lxcast_str_to_int_test: 8.4136546980 [s] (1% faster than prev. run)
cnv_lxcast_str_to_int_test: 8.5322524600 [s] (1% slower than raw above)
raw_spirit_str_to_int_test: 2.3842388060 [s] (1% faster than prev. run)
cnv_spirit_str_to_int_test: 2.3812094400 [s] (0% faster than raw above)
The results for consecutive runs varied with deviations of around 2%. Under 2% was also the deviation of the "cnv" code compared to the "raw" code. That indicates that the ['Boost.Convert] framework had no detectable running overhead with the tested compilers, hardware and deployment scenarios. The results might be different on other platforms.
[endsect]
[section:the_bigger_picture The Bigger Picture]
The presented conversion-related performance data (and potential performance-related concerns) have to be taken in perspective. A special project with serious high performance requirements is most likely to require special solutions which are most likely to cast out many generic libraries. Still, the existence of such projects did not kill or diminish in any way the importance of generic libraries. And ['Boost.Convert] is one such ['generic] library.
More so, if performance is a concern, then, in all likelihood, for the great majority of the mainstream projects ['Boost.Convert] will be far from the top on the performance profiler's list. For example, to measure conversion-related overhead relative to `std::string`-related overhead, `std::string` was replaced with a custom ['small-string] type:
[my_string_declaration]
The class above is essentially a glorified 12-bytes character buffer sufficient to accommodate string representations of 32-bit integers. Deployed with the same converters and the same algorithms:
[small_string_results]
It produced the following relative results (compiled with `gcc -O3`):
strtol int-to std::string/small-string: 1.41/0.53 seconds.
spirit int-to std::string/small-string: 1.98/1.04 seconds.
stream int-to std::string/small-string: 2.49/1.40 seconds.
The results suggest that for the projects that deal with string-related conversions and routinely deploy `std::string` facilities the conversion-related overhead will be outweighed by the overhead of `std::string`-related operations.
[endsect]
[endsect]