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

source code of boost/libs/convert/test/performance.cpp