1 | // Boost.Convert test and usage example |
2 | // Copyright (c) 2009-2014 Vladimir Batov. |
3 | // Use, modification and distribution are subject to the Boost Software License, |
4 | // Version 1.0. See http://www.boost.org/LICENSE_1_0.txt. |
5 | |
6 | #include "./test.hpp" |
7 | #include "./prepare.hpp" |
8 | |
9 | #include <boost/convert.hpp> |
10 | #include <boost/convert/stream.hpp> |
11 | #include <boost/convert/printf.hpp> |
12 | #include <boost/convert/strtol.hpp> |
13 | #include <boost/convert/spirit.hpp> |
14 | #include <boost/convert/lexical_cast.hpp> |
15 | #include <boost/detail/lightweight_test.hpp> |
16 | #include <boost/timer/timer.hpp> |
17 | #include <boost/array.hpp> |
18 | #include <boost/random/mersenne_twister.hpp> |
19 | #include <boost/random/uniform_int_distribution.hpp> |
20 | #include <cstdlib> |
21 | #include <cstdio> |
22 | |
23 | using std::string; |
24 | using boost::convert; |
25 | |
26 | namespace cnv = boost::cnv; |
27 | namespace arg = boost::cnv::parameter; |
28 | |
29 | namespace { namespace local |
30 | { |
31 | template<typename Type> |
32 | struct array |
33 | { |
34 | typedef boost::array<Type, 20> type; |
35 | }; |
36 | template<typename T> static typename array<T>::type const& get(); |
37 | |
38 | static int const num_cycles = 1000000; |
39 | int sum = 0; |
40 | |
41 | struct timer : public boost::timer::cpu_timer |
42 | { |
43 | typedef timer this_type; |
44 | typedef boost::timer::cpu_timer base_type; |
45 | |
46 | double value() const |
47 | { |
48 | boost::timer::cpu_times times = base_type::elapsed(); |
49 | int const use_sum = (sum % 2) ? 0 : (sum % 2); BOOST_TEST(use_sum == 0); |
50 | |
51 | return double(times.user + times.system) / 1000000000 + use_sum; |
52 | } |
53 | }; |
54 | template< typename Type, typename Cnv> static double str_to (Cnv const&); |
55 | template<typename S, typename Type, typename Cnv> static double to_str (Cnv const&); |
56 | |
57 | template<> |
58 | local::array<int>::type const& |
59 | get<int>() |
60 | { |
61 | static array<int>::type ints; |
62 | static bool filled; |
63 | |
64 | if (!filled) |
65 | { |
66 | boost::random::mt19937 gen (::time(timer: 0)); |
67 | boost::random::uniform_int_distribution<> dist (INT_MIN, INT_MAX); // INT_MAX(32) = 2,147,483,647 |
68 | |
69 | for (size_t k = 0; k < ints.size(); ++k) |
70 | ints[k] = dist(gen); |
71 | |
72 | filled = true; |
73 | } |
74 | return ints; |
75 | } |
76 | template<> |
77 | array<long int>::type const& |
78 | get<long int>() |
79 | { |
80 | static array<long int>::type ints; |
81 | static bool filled; |
82 | |
83 | if (!filled) |
84 | { |
85 | boost::random::mt19937 gen (::time(timer: 0)); |
86 | boost::random::uniform_int_distribution<> dist (INT_MIN, INT_MAX); // INT_MAX(32) = 2147483647 |
87 | |
88 | for (size_t k = 0; k < ints.size(); ++k) |
89 | ints[k] = dist(gen); |
90 | |
91 | filled = true; |
92 | } |
93 | return ints; |
94 | } |
95 | template<> |
96 | array<double>::type const& |
97 | get<double>() |
98 | { |
99 | static array<double>::type dbls; |
100 | static bool filled; |
101 | |
102 | if (!filled) |
103 | { |
104 | boost::random::mt19937 gen (::time(timer: 0)); |
105 | boost::random::uniform_int_distribution<> dist (INT_MIN, INT_MAX); // INT_MAX(32) = 2147483647 |
106 | |
107 | for (size_t k = 0; k < dbls.size(); ++k) |
108 | dbls[k] = double(dist(gen)) + 0.7654321; |
109 | |
110 | filled = true; |
111 | } |
112 | return dbls; |
113 | } |
114 | }} |
115 | |
116 | struct raw_str_to_int_spirit |
117 | { |
118 | int operator()(char const* str) const |
119 | { |
120 | char const* beg = str; |
121 | char const* end = beg + strlen(s: str); |
122 | int result; |
123 | |
124 | if (boost::spirit::qi::parse(first&: beg, last: end, expr: boost::spirit::int_, attr&: result)) |
125 | if (beg == end) // ensure the whole string was parsed |
126 | return result; |
127 | |
128 | return (BOOST_ASSERT(0), result); |
129 | } |
130 | }; |
131 | |
132 | struct raw_str_to_int_lxcast |
133 | { |
134 | int operator()(char const* str) const |
135 | { |
136 | return boost::lexical_cast<int>(arg: str); |
137 | } |
138 | }; |
139 | |
140 | template<typename Type, typename Converter> |
141 | double |
142 | raw_str_to(Converter const& cnv) |
143 | { |
144 | local::strings strings = local::get_strs(); // Create strings on the stack |
145 | int const size = strings.size(); |
146 | local::timer timer; |
147 | |
148 | for (int t = 0; t < local::num_cycles; ++t) |
149 | for (int k = 0; k < size; ++k) |
150 | local::sum += cnv(strings[k].c_str()); |
151 | |
152 | return timer.value(); |
153 | } |
154 | |
155 | template<typename Type, typename Converter> |
156 | double |
157 | local::str_to(Converter const& try_converter) |
158 | { |
159 | local::strings strings = local::get_strs(); // Create strings on the stack |
160 | int const size = strings.size(); |
161 | local::timer timer; |
162 | |
163 | for (int t = 0; t < local::num_cycles; ++t) |
164 | for (int k = 0; k < size; ++k) |
165 | local::sum += boost::convert<Type>(strings[k].c_str(), try_converter).value(); |
166 | |
167 | return timer.value(); |
168 | } |
169 | |
170 | template<typename string_type, typename Type, typename Converter> |
171 | double |
172 | local::to_str(Converter const& try_converter) |
173 | { |
174 | typedef typename local::array<Type>::type collection; |
175 | |
176 | collection values = local::get<Type>(); |
177 | int const size = values.size(); |
178 | local::timer timer; |
179 | |
180 | for (int t = 0; t < local::num_cycles; ++t) |
181 | for (int k = 0; k < size; ++k) |
182 | local::sum += *boost::convert<string_type>(Type(values[k]), try_converter).value().begin(); |
183 | |
184 | return timer.value(); |
185 | } |
186 | |
187 | template<typename Converter> |
188 | double |
189 | performance_str_to_type(Converter const& try_converter) |
190 | { |
191 | char const* input[] = { "no" , "up" , "dn" }; |
192 | local::timer timer; |
193 | |
194 | for (int k = 0; k < local::num_cycles; ++k) |
195 | { |
196 | change chg = boost::convert<change>(input[k % 3], try_converter).value(); |
197 | int res = chg.value(); |
198 | |
199 | BOOST_TEST(res == k % 3); |
200 | |
201 | local::sum += res; // Make sure chg is not optimized out |
202 | } |
203 | return timer.value(); |
204 | } |
205 | |
206 | template<typename Converter> |
207 | double |
208 | performance_type_to_str(Converter const& try_converter) |
209 | { |
210 | boost::array<change, 3> input = {.elems: { change::no, change::up, change::dn }}; |
211 | boost::array<string, 3> results = {.elems: { "no" , "up" , "dn" }}; |
212 | local::timer timer; |
213 | |
214 | for (int k = 0; k < local::num_cycles; ++k) |
215 | { |
216 | string res = boost::convert<string>(input[k % 3], try_converter).value(); |
217 | |
218 | BOOST_TEST(res == results[k % 3]); |
219 | |
220 | local::sum += res[0]; // Make sure res is not optimized out |
221 | } |
222 | return timer.value(); |
223 | } |
224 | |
225 | template<typename Raw, typename Cnv> |
226 | void |
227 | performance_comparative(Raw const& raw, Cnv const& cnv, char const* txt) |
228 | { |
229 | int const num_tries = 5; |
230 | double cnv_time = 0; |
231 | double raw_time = 0; |
232 | |
233 | for (int k = 0; k < num_tries; ++k) cnv_time += local::str_to<int>(cnv); |
234 | for (int k = 0; k < num_tries; ++k) raw_time += raw_str_to<int>(raw); |
235 | |
236 | cnv_time /= num_tries; |
237 | raw_time /= num_tries; |
238 | |
239 | double change = 100 * (1 - cnv_time / raw_time); |
240 | |
241 | printf(format: "str-to-int: %s raw/cnv=%.2f/%.2f seconds (%.2f%%).\n" , txt, raw_time, cnv_time, change); |
242 | } |
243 | |
244 | int |
245 | main(int, char const* []) |
246 | { |
247 | printf(format: "Started performance tests...\n" ); |
248 | |
249 | printf(format: "str-to-int: spirit/strtol/lcast/scanf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n" , |
250 | local::str_to<int>(try_converter: boost::cnv::spirit()), |
251 | local::str_to<int>(try_converter: boost::cnv::strtol()), |
252 | local::str_to<int>(try_converter: boost::cnv::lexical_cast()), |
253 | local::str_to<int>(try_converter: boost::cnv::printf()), |
254 | local::str_to<int>(try_converter: boost::cnv::cstream())); |
255 | printf(format: "str-to-lng: spirit/strtol/lcast/scanf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n" , |
256 | local::str_to<long int>(try_converter: boost::cnv::spirit()), |
257 | local::str_to<long int>(try_converter: boost::cnv::strtol()), |
258 | local::str_to<long int>(try_converter: boost::cnv::lexical_cast()), |
259 | local::str_to<long int>(try_converter: boost::cnv::printf()), |
260 | local::str_to<long int>(try_converter: boost::cnv::cstream())); |
261 | printf(format: "str-to-dbl: spirit/strtol/lcast/scanf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n" , |
262 | local::str_to<double>(try_converter: boost::cnv::spirit()), |
263 | local::str_to<double>(try_converter: boost::cnv::strtol()), |
264 | local::str_to<double>(try_converter: boost::cnv::lexical_cast()), |
265 | local::str_to<double>(try_converter: boost::cnv::printf()), |
266 | local::str_to<double>(try_converter: boost::cnv::cstream())); |
267 | |
268 | printf(format: "int-to-str: spirit/strtol/lcast/prntf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n" , |
269 | local::to_str<std::string, int>(try_converter: boost::cnv::spirit()), |
270 | local::to_str<std::string, int>(try_converter: boost::cnv::strtol()), |
271 | local::to_str<std::string, int>(try_converter: boost::cnv::lexical_cast()), |
272 | local::to_str<std::string, int>(try_converter: boost::cnv::printf()), |
273 | local::to_str<std::string, int>(try_converter: boost::cnv::cstream())); |
274 | printf(format: "lng-to-str: spirit/strtol/lcast/prntf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n" , |
275 | local::to_str<std::string, long int>(try_converter: boost::cnv::spirit()), |
276 | local::to_str<std::string, long int>(try_converter: boost::cnv::strtol()), |
277 | local::to_str<std::string, long int>(try_converter: boost::cnv::lexical_cast()), |
278 | local::to_str<std::string, long int>(try_converter: boost::cnv::printf()), |
279 | local::to_str<std::string, long int>(try_converter: boost::cnv::cstream())); |
280 | printf(format: "dbl-to-str: spirit/strtol/lcast/prntf/stream=%7.2f/%7.2f/%7.2f/%7.2f/%7.2f seconds.\n" , |
281 | local::to_str<std::string, double>(try_converter: boost::cnv::spirit()), |
282 | local::to_str<std::string, double>(try_converter: boost::cnv::strtol()(arg::precision = 6)), |
283 | local::to_str<std::string, double>(try_converter: boost::cnv::lexical_cast()), |
284 | local::to_str<std::string, double>(try_converter: boost::cnv::printf()(arg::precision = 6)), |
285 | local::to_str<std::string, double>(try_converter: boost::cnv::cstream()(arg::precision = 6))); |
286 | |
287 | printf(format: "str-to-user-type: lcast/stream/strtol=%.2f/%.2f/%.2f seconds.\n" , |
288 | performance_str_to_type(try_converter: boost::cnv::lexical_cast()), |
289 | performance_str_to_type(try_converter: boost::cnv::cstream()), |
290 | performance_str_to_type(try_converter: boost::cnv::strtol())); |
291 | printf(format: "user-type-to-str: lcast/stream/strtol=%.2f/%.2f/%.2f seconds.\n" , |
292 | performance_type_to_str(try_converter: boost::cnv::lexical_cast()), |
293 | performance_type_to_str(try_converter: boost::cnv::cstream()), |
294 | performance_type_to_str(try_converter: boost::cnv::strtol())); |
295 | |
296 | //[small_string_results |
297 | printf(format: "strtol int-to std::string/small-string: %.2f/%.2f seconds.\n" , |
298 | local::to_str<std::string, int>(try_converter: boost::cnv::strtol()), |
299 | local::to_str< my_string, int>(try_converter: boost::cnv::strtol())); |
300 | printf(format: "spirit int-to std::string/small-string: %.2f/%.2f seconds.\n" , |
301 | local::to_str<std::string, int>(try_converter: boost::cnv::spirit()), |
302 | local::to_str< my_string, int>(try_converter: boost::cnv::spirit())); |
303 | printf(format: "stream int-to std::string/small-string: %.2f/%.2f seconds.\n" , |
304 | local::to_str<std::string, int>(try_converter: boost::cnv::cstream()), |
305 | local::to_str< my_string, int>(try_converter: boost::cnv::cstream())); |
306 | //] |
307 | performance_comparative(raw: raw_str_to_int_spirit(), cnv: boost::cnv::spirit(), txt: "spirit" ); |
308 | performance_comparative(raw: raw_str_to_int_lxcast(), cnv: boost::cnv::lexical_cast(), txt: "lxcast" ); |
309 | |
310 | return boost::report_errors(); |
311 | } |
312 | |