1 ///////////////////////////////////////////////////////////////
2 //  Copyright 2011 John Maddock. Distributed under the Boost
3 //  Software License, Version 1.0. (See accompanying file
4 //  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
5 
6 #define BOOST_CHRONO_HEADER_ONLY
7 
8 #ifdef _MSC_VER
9 #define _SCL_SECURE_NO_WARNINGS
10 #endif
11 
12 #if !defined(TEST_MPF) && !defined(TEST_MPZ) && \
13     !defined(TEST_CPP_DEC_FLOAT) && !defined(TEST_MPFR) && !defined(TEST_MPQ) && !defined(TEST_TOMMATH) && !defined(TEST_TOMMATH_BOOST_RATIONAL) && !defined(TEST_MPZ_BOOST_RATIONAL) && !defined(TEST_CPP_INT) && !defined(TEST_CPP_INT_RATIONAL) && !defined(TEST_CPP_BIN_FLOAT)
14 #define TEST_MPF
15 #define TEST_MPZ
16 #define TEST_MPQ
17 #define TEST_MPFR
18 #define TEST_CPP_DEC_FLOAT
19 #define TEST_MPQ
20 #define TEST_TOMMATH
21 #define TEST_CPP_INT
22 #define TEST_CPP_INT_RATIONAL
23 #define TEST_CPP_BIN_FLOAT
24 
25 #ifdef _MSC_VER
26 #pragma message("CAUTION!!: No backend type specified so testing everything.... this will take some time!!")
27 #endif
28 #ifdef __GNUC__
29 #pragma warning "CAUTION!!: No backend type specified so testing everything.... this will take some time!!"
30 #endif
31 
32 #endif
33 
34 #if defined(TEST_MPF) || defined(TEST_MPZ) || defined(TEST_MPQ) || defined(TEST_MPZ_BOOST_RATIONAL)
35 #include <boost/multiprecision/gmp.hpp>
36 #include <boost/multiprecision/rational_adaptor.hpp>
37 #endif
38 #ifdef TEST_CPP_DEC_FLOAT
39 #include <boost/multiprecision/cpp_dec_float.hpp>
40 #endif
41 #ifdef TEST_CPP_BIN_FLOAT
42 #include <boost/multiprecision/cpp_bin_float.hpp>
43 #endif
44 #if defined(TEST_MPFR)
45 #include <boost/multiprecision/mpfr.hpp>
46 #endif
47 #if defined(TEST_TOMMATH) || defined(TEST_TOMMATH_BOOST_RATIONAL)
48 #include <boost/multiprecision/tommath.hpp>
49 #include <boost/multiprecision/rational_adaptor.hpp>
50 #endif
51 #if defined(TEST_CPP_INT) || defined(TEST_CPP_INT_RATIONAL)
52 #include <boost/multiprecision/cpp_int.hpp>
53 #endif
54 
55 #include <boost/chrono.hpp>
56 #include <vector>
57 #include <map>
58 #include <string>
59 #include <cstring>
60 #include <cctype>
61 #include <iostream>
62 #include <iomanip>
63 #include <boost/random/mersenne_twister.hpp>
64 #include <boost/random/uniform_int.hpp>
65 
66 template <class Clock>
67 struct stopwatch
68 {
69    typedef typename Clock::duration duration;
stopwatchstopwatch70    stopwatch()
71    {
72       m_start = Clock::now();
73    }
elapsedstopwatch74    duration elapsed()
75    {
76       return Clock::now() - m_start;
77    }
resetstopwatch78    void reset()
79    {
80       m_start = Clock::now();
81    }
82 
83  private:
84    typename Clock::time_point m_start;
85 };
86 
87 unsigned bits_wanted; // for integer types
88 
89 template <class T, int Type>
90 struct tester
91 {
testertester92    tester()
93    {
94       a.assign(500, 0);
95       for (int i = 0; i < 500; ++i)
96       {
97          b.push_back(generate_random());
98          c.push_back(generate_random());
99          small.push_back(gen());
100       }
101    }
test_addtester102    double test_add()
103    {
104       stopwatch<boost::chrono::high_resolution_clock> w;
105       for (unsigned i = 0; i < 1000; ++i)
106       {
107          for (unsigned i = 0; i < b.size(); ++i)
108             a[i] = b[i] + c[i];
109       }
110       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
111    }
test_subtracttester112    double test_subtract()
113    {
114       stopwatch<boost::chrono::high_resolution_clock> w;
115       for (unsigned i = 0; i < 1000; ++i)
116       {
117          for (unsigned i = 0; i < b.size(); ++i)
118             a[i] = b[i] - c[i];
119       }
120       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
121    }
test_add_inttester122    double test_add_int()
123    {
124       stopwatch<boost::chrono::high_resolution_clock> w;
125       for (unsigned i = 0; i < 1000; ++i)
126       {
127          for (unsigned i = 0; i < b.size(); ++i)
128             a[i] = b[i] + 1;
129       }
130       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
131    }
test_subtract_inttester132    double test_subtract_int()
133    {
134       stopwatch<boost::chrono::high_resolution_clock> w;
135       for (unsigned i = 0; i < 1000; ++i)
136       {
137          for (unsigned i = 0; i < b.size(); ++i)
138             a[i] = b[i] - 1;
139       }
140       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
141    }
test_multiplytester142    double test_multiply()
143    {
144       stopwatch<boost::chrono::high_resolution_clock> w;
145       for (unsigned i = 0; i < 1000; ++i)
146       {
147          for (unsigned k = 0; k < b.size(); ++k)
148             a[k] = b[k] * c[k];
149       }
150       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
151    }
test_multiply_inttester152    double test_multiply_int()
153    {
154       stopwatch<boost::chrono::high_resolution_clock> w;
155       for (unsigned i = 0; i < 1000; ++i)
156       {
157          for (unsigned i = 0; i < b.size(); ++i)
158             a[i] = b[i] * 3;
159       }
160       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
161    }
test_dividetester162    double test_divide()
163    {
164       stopwatch<boost::chrono::high_resolution_clock> w;
165       for (unsigned i = 0; i < 1000; ++i)
166       {
167          for (unsigned i = 0; i < b.size(); ++i)
168             a[i] = b[i] / c[i] + b[i] / small[i];
169       }
170       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
171    }
test_divide_inttester172    double test_divide_int()
173    {
174       stopwatch<boost::chrono::high_resolution_clock> w;
175       for (unsigned i = 0; i < 1000; ++i)
176       {
177          for (unsigned i = 0; i < b.size(); ++i)
178             a[i] = b[i] / 3;
179       }
180       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
181    }
test_strtester182    double test_str(const boost::mpl::false_&)
183    {
184       stopwatch<boost::chrono::high_resolution_clock> w;
185       for (unsigned i = 0; i < b.size(); ++i)
186          a[i] = boost::lexical_cast<T>(boost::lexical_cast<std::string>(b[i]));
187       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
188    }
test_strtester189    double test_str(const boost::mpl::true_&)
190    {
191       stopwatch<boost::chrono::high_resolution_clock> w;
192       for (unsigned i = 0; i < b.size(); ++i)
193          a[i].assign(b[i].str());
194       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
195    }
test_strtester196    double test_str()
197    {
198       return test_str(boost::is_class<T>());
199    }
200    //
201    // The following tests only work for integer types:
202    //
test_modtester203    double test_mod()
204    {
205       stopwatch<boost::chrono::high_resolution_clock> w;
206       for (unsigned i = 0; i < 1000; ++i)
207       {
208          for (unsigned i = 0; i < b.size(); ++i)
209             a[i] = b[i] % c[i] + b[i] % small[i];
210       }
211       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
212    }
test_mod_inttester213    double test_mod_int()
214    {
215       stopwatch<boost::chrono::high_resolution_clock> w;
216       for (unsigned i = 0; i < 1000; ++i)
217       {
218          for (unsigned i = 0; i < b.size(); ++i)
219             a[i] = b[i] % 254;
220       }
221       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
222    }
test_ortester223    double test_or()
224    {
225       stopwatch<boost::chrono::high_resolution_clock> w;
226       for (unsigned i = 0; i < 1000; ++i)
227       {
228          for (unsigned i = 0; i < b.size(); ++i)
229             a[i] = b[i] | c[i];
230       }
231       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
232    }
test_or_inttester233    double test_or_int()
234    {
235       stopwatch<boost::chrono::high_resolution_clock> w;
236       for (unsigned i = 0; i < 1000; ++i)
237       {
238          for (unsigned i = 0; i < b.size(); ++i)
239             a[i] = b[i] | 234;
240       }
241       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
242    }
test_andtester243    double test_and()
244    {
245       stopwatch<boost::chrono::high_resolution_clock> w;
246       for (unsigned i = 0; i < 1000; ++i)
247       {
248          for (unsigned i = 0; i < b.size(); ++i)
249             a[i] = b[i] & c[i];
250       }
251       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
252    }
test_and_inttester253    double test_and_int()
254    {
255       stopwatch<boost::chrono::high_resolution_clock> w;
256       for (unsigned i = 0; i < 1000; ++i)
257       {
258          for (unsigned i = 0; i < b.size(); ++i)
259             a[i] = b[i] & 234;
260       }
261       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
262    }
test_xortester263    double test_xor()
264    {
265       stopwatch<boost::chrono::high_resolution_clock> w;
266       for (unsigned i = 0; i < 1000; ++i)
267       {
268          for (unsigned i = 0; i < b.size(); ++i)
269             a[i] = b[i] ^ c[i];
270       }
271       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
272    }
test_xor_inttester273    double test_xor_int()
274    {
275       stopwatch<boost::chrono::high_resolution_clock> w;
276       for (unsigned i = 0; i < 1000; ++i)
277       {
278          for (unsigned i = 0; i < b.size(); ++i)
279             a[i] = b[i] ^ 234;
280       }
281       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
282    }
test_complementtester283    double test_complement()
284    {
285       stopwatch<boost::chrono::high_resolution_clock> w;
286       for (unsigned i = 0; i < 1000; ++i)
287       {
288          for (unsigned i = 0; i < b.size(); ++i)
289             a[i] = ~b[i];
290       }
291       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
292    }
test_left_shifttester293    double test_left_shift()
294    {
295       int                                             max_shift = std::numeric_limits<T>::is_bounded ? std::numeric_limits<T>::digits : bits_wanted;
296       int                                             shift     = 0;
297       stopwatch<boost::chrono::high_resolution_clock> w;
298       for (unsigned i = 0; i < 1000; ++i)
299       {
300          for (unsigned i = 0; i < b.size(); ++i)
301             a[i] = b[i] << (shift++ % max_shift);
302       }
303       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
304    }
test_right_shifttester305    double test_right_shift()
306    {
307       int                                             max_shift = 2 + std::numeric_limits<T>::is_bounded ? std::numeric_limits<T>::digits : bits_wanted;
308       int                                             shift     = 0;
309       stopwatch<boost::chrono::high_resolution_clock> w;
310       for (unsigned i = 0; i < 1000; ++i)
311       {
312          for (unsigned i = 0; i < b.size(); ++i)
313             a[i] = b[i] >> (shift++) % max_shift;
314       }
315       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
316    }
test_gcdtester317    double test_gcd()
318    {
319       using boost::integer::gcd;
320       stopwatch<boost::chrono::high_resolution_clock> w;
321       for (unsigned i = 0; i < 1000; ++i)
322       {
323          for (unsigned i = 0; i < b.size(); ++i)
324             a[i] = gcd(b[i], c[i]);
325       }
326       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
327    }
test_powmtester328    double test_powm()
329    {
330       stopwatch<boost::chrono::high_resolution_clock> w;
331       for (unsigned i = 0; i < 25; ++i)
332       {
333          for (unsigned i = 0; i < b.size(); ++i)
334             a[i] = powm(b[i], b[i] / 2, c[i]);
335       }
336       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
337    }
test_constructtester338    double test_construct()
339    {
340       std::allocator<T>                               a;
341       T*                                              pt = a.allocate(1000);
342       stopwatch<boost::chrono::high_resolution_clock> w;
343       for (unsigned i = 0; i < 1000; ++i)
344       {
345          for (unsigned i = 0; i < 1000; ++i)
346             new (pt + i) T();
347          for (unsigned i = 0; i < 1000; ++i)
348             a.destroy(pt + i);
349       }
350       double result = boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
351       a.deallocate(pt, 1000);
352       return result;
353    }
test_construct_unsignedtester354    double test_construct_unsigned()
355    {
356       std::allocator<T>                               a;
357       T*                                              pt = a.allocate(1000);
358       stopwatch<boost::chrono::high_resolution_clock> w;
359       for (unsigned i = 0; i < 1000; ++i)
360       {
361          for (unsigned i = 0; i < 1000; ++i)
362             new (pt + i) T(i);
363          for (unsigned i = 0; i < 1000; ++i)
364             a.destroy(pt + i);
365       }
366       double result = boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
367       a.deallocate(pt, 1000);
368       return result;
369    }
test_construct_unsigned_lltester370    double test_construct_unsigned_ll()
371    {
372       std::allocator<T>                               a;
373       T*                                              pt = a.allocate(1000);
374       stopwatch<boost::chrono::high_resolution_clock> w;
375       for (unsigned i = 0; i < 1000; ++i)
376       {
377          for (unsigned long long j = 0; j < 1000; ++j)
378             new (pt + j) T(j);
379          for (unsigned j = 0; j < 1000; ++j)
380             a.destroy(pt + j);
381       }
382       double result = boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
383       a.deallocate(pt, 1000);
384       return result;
385    }
386 
387    //
388    // Hetero operations:
389    //
390    template <class U>
get_hetero_test_valuetester391    static U get_hetero_test_value(boost::mpl::false_ const&)
392    {
393       return U(2) / 3;
394    }
395    template <class U>
get_hetero_test_valuetester396    static U get_hetero_test_value(boost::mpl::true_ const&)
397    {
398       return (std::numeric_limits<U>::max)() >> 4;
399    }
400    template <class U>
get_hetero_test_valuetester401    static U get_hetero_test_value()
402    {
403       return get_hetero_test_value<U>(boost::is_integral<U>());
404    }
405    template <class U>
test_multiply_heterotester406    double test_multiply_hetero()
407    {
408       static const U                                  val = get_hetero_test_value<U>();
409       stopwatch<boost::chrono::high_resolution_clock> w;
410       for (unsigned i = 0; i < 1000; ++i)
411       {
412          for (unsigned i = 0; i < b.size(); ++i)
413             a[i] = b[i] * val;
414       }
415       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
416    }
417    template <class U>
test_inplace_multiply_heterotester418    double test_inplace_multiply_hetero()
419    {
420       static const U val = get_hetero_test_value<U>();
421       for (unsigned i = 0; i < b.size(); ++i)
422          a[i] = b[i];
423       stopwatch<boost::chrono::high_resolution_clock> w;
424       for (unsigned i = 0; i < 1000; ++i)
425       {
426          for (unsigned i = 0; i < b.size(); ++i)
427             a[i] *= val;
428       }
429       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
430    }
431    template <class U>
test_add_heterotester432    double test_add_hetero()
433    {
434       static const U                                  val = get_hetero_test_value<U>();
435       stopwatch<boost::chrono::high_resolution_clock> w;
436       for (unsigned i = 0; i < 1000; ++i)
437       {
438          for (unsigned i = 0; i < b.size(); ++i)
439             a[i] = b[i] + val;
440       }
441       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
442    }
443    template <class U>
test_inplace_add_heterotester444    double test_inplace_add_hetero()
445    {
446       static const U val = get_hetero_test_value<U>();
447       for (unsigned i = 0; i < b.size(); ++i)
448          a[i] = b[i];
449       stopwatch<boost::chrono::high_resolution_clock> w;
450       for (unsigned i = 0; i < 1000; ++i)
451       {
452          for (unsigned i = 0; i < b.size(); ++i)
453             a[i] += val;
454       }
455       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
456    }
457    template <class U>
test_subtract_heterotester458    double test_subtract_hetero()
459    {
460       static const U                                  val = get_hetero_test_value<U>();
461       stopwatch<boost::chrono::high_resolution_clock> w;
462       for (unsigned i = 0; i < 1000; ++i)
463       {
464          for (unsigned i = 0; i < b.size(); ++i)
465             a[i] = b[i] - val;
466       }
467       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
468    }
469    template <class U>
test_inplace_subtract_heterotester470    double test_inplace_subtract_hetero()
471    {
472       static const U val = get_hetero_test_value<U>();
473       for (unsigned i = 0; i < b.size(); ++i)
474          a[i] = b[i];
475       stopwatch<boost::chrono::high_resolution_clock> w;
476       for (unsigned i = 0; i < 1000; ++i)
477       {
478          for (unsigned i = 0; i < b.size(); ++i)
479             a[i] -= val;
480       }
481       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
482    }
483    template <class U>
test_divide_heterotester484    double test_divide_hetero()
485    {
486       static const U                                  val = get_hetero_test_value<U>();
487       stopwatch<boost::chrono::high_resolution_clock> w;
488       for (unsigned i = 0; i < 1000; ++i)
489       {
490          for (unsigned i = 0; i < b.size(); ++i)
491             a[i] = b[i] / val;
492       }
493       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
494    }
495    template <class U>
test_inplace_divide_heterotester496    double test_inplace_divide_hetero()
497    {
498       static const U val = get_hetero_test_value<U>();
499       for (unsigned i = 0; i < b.size(); ++i)
500          a[i] = b[i];
501       stopwatch<boost::chrono::high_resolution_clock> w;
502       for (unsigned i = 0; i < 1000; ++i)
503       {
504          for (unsigned i = 0; i < b.size(); ++i)
505             a[i] /= val;
506       }
507       return boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
508    }
509 
510  private:
generate_randomtester511    T generate_random()
512    {
513       return generate_random(boost::mpl::int_<Type>());
514    }
generate_randomtester515    T generate_random(const boost::mpl::int_<boost::multiprecision::number_kind_floating_point>&)
516    {
517       T val      = gen();
518       T prev_val = -1;
519       while (val != prev_val)
520       {
521          val *= (gen.max)();
522          prev_val = val;
523          val += gen();
524       }
525       int e;
526       val = frexp(val, &e);
527 
528       typedef typename T::backend_type::exponent_type        e_type;
529       static boost::random::uniform_int_distribution<e_type> ui(-30, 30);
530       return ldexp(val, static_cast<int>(ui(gen)));
531    }
generate_randomtester532    T generate_random(const boost::mpl::int_<boost::multiprecision::number_kind_integer>&)
533    {
534       typedef boost::random::mt19937::result_type random_type;
535 
536       T        max_val;
537       unsigned digits;
538       if (std::numeric_limits<T>::is_bounded)
539       {
540          max_val = (std::numeric_limits<T>::max)();
541          digits  = std::numeric_limits<T>::digits;
542       }
543       else
544       {
545          max_val = T(1) << bits_wanted;
546          digits  = bits_wanted;
547       }
548 
549       unsigned bits_per_r_val = std::numeric_limits<random_type>::digits - 1;
550       while ((random_type(1) << bits_per_r_val) > (gen.max)())
551          --bits_per_r_val;
552 
553       unsigned terms_needed = digits / bits_per_r_val + 1;
554 
555       T val = 0;
556       for (unsigned i = 0; i < terms_needed; ++i)
557       {
558          val *= (gen.max)();
559          val += gen();
560       }
561       val %= max_val;
562       return val;
563    }
generate_randomtester564    T generate_random(const boost::mpl::int_<boost::multiprecision::number_kind_rational>&)
565    {
566       typedef boost::random::mt19937::result_type                     random_type;
567       typedef typename boost::multiprecision::component_type<T>::type IntType;
568 
569       IntType  max_val;
570       unsigned digits;
571       if (std::numeric_limits<IntType>::is_bounded)
572       {
573          max_val = (std::numeric_limits<IntType>::max)();
574          digits  = std::numeric_limits<IntType>::digits;
575       }
576       else
577       {
578          max_val = IntType(1) << bits_wanted;
579          digits  = bits_wanted;
580       }
581 
582       unsigned bits_per_r_val = std::numeric_limits<random_type>::digits - 1;
583       while ((random_type(1) << bits_per_r_val) > (gen.max)())
584          --bits_per_r_val;
585 
586       unsigned terms_needed = digits / bits_per_r_val + 1;
587 
588       IntType val   = 0;
589       IntType denom = 0;
590       for (unsigned i = 0; i < terms_needed; ++i)
591       {
592          val *= (gen.max)();
593          val += gen();
594       }
595       for (unsigned i = 0; i < terms_needed; ++i)
596       {
597          denom *= (gen.max)();
598          denom += gen();
599       }
600       if (denom == 0)
601          denom = 1;
602       val %= max_val;
603       denom %= max_val;
604       return T(val, denom);
605    }
606    std::vector<T>                a, b, c, small;
607    static boost::random::mt19937 gen;
608 };
609 
610 template <class N, int V>
611 boost::random::mt19937 tester<N, V>::gen;
612 
category_name(const boost::mpl::int_<boost::multiprecision::number_kind_integer> &)613 const char* category_name(const boost::mpl::int_<boost::multiprecision::number_kind_integer>&)
614 {
615    return "integer";
616 }
category_name(const boost::mpl::int_<boost::multiprecision::number_kind_floating_point> &)617 const char* category_name(const boost::mpl::int_<boost::multiprecision::number_kind_floating_point>&)
618 {
619    return "float";
620 }
category_name(const boost::mpl::int_<boost::multiprecision::number_kind_rational> &)621 const char* category_name(const boost::mpl::int_<boost::multiprecision::number_kind_rational>&)
622 {
623    return "rational";
624 }
625 
626 //
627 // Keys in order are:
628 // Category
629 // Operator
630 // Type
631 // Precision
632 // Time
633 //
634 std::map<std::string, std::map<std::string, std::map<std::string, std::map<int, double> > > > result_table;
635 
report_result(const char * cat,const char * type,const char * op,unsigned precision,double time)636 void report_result(const char* cat, const char* type, const char* op, unsigned precision, double time)
637 {
638    std::cout << std::left << std::setw(15) << type << std::setw(10) << precision << std::setw(35) << op << time << std::endl;
639    result_table[cat][op][type][precision] = time;
640 }
641 
642 template <class Number, int N>
test_int_ops(tester<Number,N> & t,const char * type,unsigned precision,const boost::mpl::int_<boost::multiprecision::number_kind_integer> &)643 void test_int_ops(tester<Number, N>& t, const char* type, unsigned precision, const boost::mpl::int_<boost::multiprecision::number_kind_integer>&)
644 {
645    const char* cat = "integer";
646    report_result(cat, type, "%", precision, t.test_mod());
647    report_result(cat, type, "|", precision, t.test_or());
648    report_result(cat, type, "&", precision, t.test_and());
649    report_result(cat, type, "^", precision, t.test_xor());
650    //report_result(cat, type, "~", precision, t.test_complement());
651    report_result(cat, type, "<<", precision, t.test_left_shift());
652    report_result(cat, type, ">>", precision, t.test_right_shift());
653    // integer ops:
654    report_result(cat, type, "%(int)", precision, t.test_mod_int());
655    report_result(cat, type, "|(int)", precision, t.test_or_int());
656    report_result(cat, type, "&(int)", precision, t.test_and_int());
657    report_result(cat, type, "^(int)", precision, t.test_xor_int());
658    report_result(cat, type, "gcd", precision, t.test_gcd());
659    report_result(cat, type, "powm", precision, t.test_powm());
660 }
661 template <class Number, int N, class U>
test_int_ops(tester<Number,N> & t,const char * type,unsigned precision,const U &)662 void test_int_ops(tester<Number, N>& t, const char* type, unsigned precision, const U&)
663 {
664 }
665 
666 template <class Number>
test(const char * type,unsigned precision)667 void test(const char* type, unsigned precision)
668 {
669    bits_wanted = precision;
670    tester<Number, boost::multiprecision::number_category<Number>::value> t;
671    const char*                                                           cat = category_name(typename boost::multiprecision::number_category<Number>::type());
672    //
673    // call t.test_multiply() first so that the destination operands are
674    // forced to perform whatever memory allocation may be needed.  That way
675    // we measure only algorithm performance, and not memory allocation effects.
676    //
677    t.test_multiply();
678    //
679    // Now the actual tests:
680    //
681    report_result(cat, type, "+", precision, t.test_add());
682    report_result(cat, type, "-", precision, t.test_subtract());
683    report_result(cat, type, "*", precision, t.test_multiply());
684    report_result(cat, type, "/", precision, t.test_divide());
685    report_result(cat, type, "str", precision, t.test_str());
686    // integer ops:
687    report_result(cat, type, "+(int)", precision, t.test_add_int());
688    report_result(cat, type, "-(int)", precision, t.test_subtract_int());
689    report_result(cat, type, "*(int)", precision, t.test_multiply_int());
690    report_result(cat, type, "/(int)", precision, t.test_divide_int());
691    // construction and destruction:
692    report_result(cat, type, "construct", precision, t.test_construct());
693    report_result(cat, type, "construct(unsigned)", precision, t.test_construct_unsigned());
694    report_result(cat, type, "construct(unsigned long long)", precision, t.test_construct_unsigned_ll());
695    test_int_ops(t, type, precision, typename boost::multiprecision::number_category<Number>::type());
696    // Hetero ops:
697    report_result(cat, type, "+(unsigned long long)", precision, t.template test_add_hetero<unsigned long long>());
698    report_result(cat, type, "-(unsigned long long)", precision, t.template test_subtract_hetero<unsigned long long>());
699    report_result(cat, type, "*(unsigned long long)", precision, t.template test_multiply_hetero<unsigned long long>());
700    report_result(cat, type, "/(unsigned long long)", precision, t.template test_divide_hetero<unsigned long long>());
701    report_result(cat, type, "+=(unsigned long long)", precision, t.template test_inplace_add_hetero<unsigned long long>());
702    report_result(cat, type, "-=(unsigned long long)", precision, t.template test_inplace_subtract_hetero<unsigned long long>());
703    report_result(cat, type, "*=(unsigned long long)", precision, t.template test_inplace_multiply_hetero<unsigned long long>());
704    report_result(cat, type, "/=(unsigned long long)", precision, t.template test_inplace_divide_hetero<unsigned long long>());
705 }
706 
quickbook_results()707 void quickbook_results()
708 {
709    //
710    // Keys in order are:
711    // Category
712    // Operator
713    // Type
714    // Precision
715    // Time
716    //
717    typedef std::map<std::string, std::map<std::string, std::map<std::string, std::map<int, double> > > >::const_iterator category_iterator;
718    typedef std::map<std::string, std::map<std::string, std::map<int, double> > >::const_iterator                         operator_iterator;
719    typedef std::map<std::string, std::map<int, double> >::const_iterator                                                 type_iterator;
720    typedef std::map<int, double>::const_iterator                                                                         precision_iterator;
721 
722    for (category_iterator i = result_table.begin(); i != result_table.end(); ++i)
723    {
724       std::string cat = i->first;
725       cat[0]          = std::toupper(cat[0]);
726       std::cout << "[section:" << i->first << "_performance " << cat << " Type Perfomance]" << std::endl;
727 
728       for (operator_iterator j = i->second.begin(); j != i->second.end(); ++j)
729       {
730          std::string op = j->first;
731          std::cout << "[table Operator " << op << std::endl;
732          std::cout << "[[Backend]";
733 
734          for (precision_iterator k = j->second.begin()->second.begin(); k != j->second.begin()->second.end(); ++k)
735          {
736             std::cout << "[" << k->first << " Bits]";
737          }
738          std::cout << "]\n";
739 
740          std::vector<double> best_times(j->second.begin()->second.size(), (std::numeric_limits<double>::max)());
741          for (unsigned m = 0; m < j->second.begin()->second.size(); ++m)
742          {
743             for (type_iterator k = j->second.begin(); k != j->second.end(); ++k)
744             {
745                precision_iterator l = k->second.begin();
746                std::advance(l, m);
747                if (best_times[m] > l->second)
748                   best_times[m] = l->second ? l->second : best_times[m];
749             }
750          }
751 
752          for (type_iterator k = j->second.begin(); k != j->second.end(); ++k)
753          {
754             std::cout << "[[" << k->first << "]";
755 
756             unsigned m = 0;
757             for (precision_iterator l = k->second.begin(); l != k->second.end(); ++l)
758             {
759                double rel_time = l->second / best_times[m];
760                if (rel_time == 1)
761                   std::cout << "[[*" << rel_time << "]";
762                else
763                   std::cout << "[" << rel_time;
764                std::cout << " (" << l->second << "s)]";
765                ++m;
766             }
767 
768             std::cout << "]\n";
769          }
770 
771          std::cout << "]\n";
772       }
773 
774       std::cout << "[endsect]" << std::endl;
775    }
776 }
777 
main()778 int main()
779 {
780 #ifdef TEST_INT64
781    test<boost::uint64_t>("boost::uint64_t", 64);
782 #endif
783 #ifdef TEST_MPF
784    test<boost::multiprecision::mpf_float_50>("gmp_float", 50);
785    test<boost::multiprecision::mpf_float_100>("gmp_float", 100);
786    test<boost::multiprecision::mpf_float_500>("gmp_float", 500);
787 #endif
788 #ifdef TEST_MPZ
789    test<boost::multiprecision::mpz_int>("gmp_int", 128);
790    test<boost::multiprecision::mpz_int>("gmp_int", 256);
791    test<boost::multiprecision::mpz_int>("gmp_int", 512);
792    test<boost::multiprecision::mpz_int>("gmp_int", 1024);
793 #endif
794 #ifdef TEST_CPP_INT
795    //test<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<64, 64, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>, boost::multiprecision::et_off> >("cpp_int(unsigned, fixed)", 64);
796    //test<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<64, 64, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>, boost::multiprecision::et_off> >("cpp_int(fixed)", 64);
797    test<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<128, 128, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>, boost::multiprecision::et_off> >("cpp_int(fixed)", 128);
798    test<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>, boost::multiprecision::et_off> >("cpp_int(fixed)", 256);
799    test<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<512, 512, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>, boost::multiprecision::et_off> >("cpp_int(fixed)", 512);
800    test<boost::multiprecision::number<boost::multiprecision::cpp_int_backend<1024, 1024, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>, boost::multiprecision::et_off> >("cpp_int(fixed)", 1024);
801 
802    test<boost::multiprecision::cpp_int>("cpp_int", 128);
803    test<boost::multiprecision::cpp_int>("cpp_int", 256);
804    test<boost::multiprecision::cpp_int>("cpp_int", 512);
805    test<boost::multiprecision::cpp_int>("cpp_int", 1024);
806 #endif
807 #ifdef TEST_CPP_INT_RATIONAL
808    test<boost::multiprecision::cpp_rational>("cpp_rational", 128);
809    test<boost::multiprecision::cpp_rational>("cpp_rational", 256);
810    test<boost::multiprecision::cpp_rational>("cpp_rational", 512);
811    test<boost::multiprecision::cpp_rational>("cpp_rational", 1024);
812 #endif
813 #ifdef TEST_MPQ
814    test<boost::multiprecision::mpq_rational>("mpq_rational", 128);
815    test<boost::multiprecision::mpq_rational>("mpq_rational", 256);
816    test<boost::multiprecision::mpq_rational>("mpq_rational", 512);
817    test<boost::multiprecision::mpq_rational>("mpq_rational", 1024);
818 #endif
819 #ifdef TEST_TOMMATH
820    test<boost::multiprecision::tom_int>("tommath_int", 128);
821    test<boost::multiprecision::tom_int>("tommath_int", 256);
822    test<boost::multiprecision::tom_int>("tommath_int", 512);
823    test<boost::multiprecision::tom_int>("tommath_int", 1024);
824    /*
825    //
826    // These are actually too slow to test!!!
827    //
828    test<boost::multiprecision::tom_rational>("tom_rational", 128);
829    test<boost::multiprecision::tom_rational>("tom_rational", 256);
830    test<boost::multiprecision::tom_rational>("tom_rational", 512);
831    test<boost::multiprecision::tom_rational>("tom_rational", 1024);
832    */
833 #endif
834 #ifdef TEST_CPP_DEC_FLOAT
835    test<boost::multiprecision::cpp_dec_float_50>("cpp_dec_float", 50);
836    test<boost::multiprecision::cpp_dec_float_100>("cpp_dec_float", 100);
837    test<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<500> > >("cpp_dec_float", 500);
838 #endif
839 #ifdef TEST_CPP_BIN_FLOAT
840    test<boost::multiprecision::cpp_bin_float_50>("cpp_bin_float", 50);
841    test<boost::multiprecision::cpp_bin_float_100>("cpp_bin_float", 100);
842    test<boost::multiprecision::number<boost::multiprecision::cpp_bin_float<500> > >("cpp_bin_float", 500);
843 #endif
844 #ifdef TEST_MPFR
845    test<boost::multiprecision::mpfr_float_50>("mpfr_float", 50);
846    test<boost::multiprecision::mpfr_float_100>("mpfr_float", 100);
847    test<boost::multiprecision::mpfr_float_500>("mpfr_float", 500);
848 #endif
849    quickbook_results();
850    return 0;
851 }
852