random_speed.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /* boost random_speed.cpp performance measurements
  2. *
  3. * Copyright Jens Maurer 2000
  4. * Distributed under the Boost Software License, Version 1.0. (See
  5. * accompanying file LICENSE_1_0.txt or copy at
  6. * http://www.boost.org/LICENSE_1_0.txt)
  7. *
  8. * $Id$
  9. */
  10. #include <iostream>
  11. #include <cstdlib>
  12. #include <string>
  13. #include <boost/config.hpp>
  14. #include <boost/random.hpp>
  15. #include <boost/progress.hpp>
  16. #include <boost/shared_ptr.hpp>
  17. /*
  18. * Configuration Section
  19. */
  20. // define if your C library supports the non-standard drand48 family
  21. //#define HAVE_DRAND48
  22. // define if you have the original mt19937int.c (with commented out main())
  23. #undef HAVE_MT19937INT_C
  24. // define if you have the original mt19937ar.c (with commented out main())
  25. // #define HAVE_MT19937AR_C
  26. // set to your CPU frequency
  27. static const double cpu_frequency = 1.87 * 1e9;
  28. /*
  29. * End of Configuration Section
  30. */
  31. /*
  32. * General portability note:
  33. * MSVC mis-compiles explicit function template instantiations.
  34. * For example, f<A>() and f<B>() are both compiled to call f<A>().
  35. * BCC is unable to implicitly convert a "const char *" to a std::string
  36. * when using explicit function template instantiations.
  37. *
  38. * Therefore, avoid explicit function template instantiations.
  39. */
  40. // provides a run-time configurable linear congruential generator, just
  41. // for comparison
  42. template<class IntType>
  43. class linear_congruential
  44. {
  45. public:
  46. typedef IntType result_type;
  47. BOOST_STATIC_CONSTANT(bool, has_fixed_range = false);
  48. linear_congruential(IntType x0, IntType a, IntType c, IntType m)
  49. : _x(x0), _a(a), _c(c), _m(m) { }
  50. // compiler-generated copy ctor and assignment operator are fine
  51. void seed(IntType x0, IntType a, IntType c, IntType m)
  52. { _x = x0; _a = a; _c = c; _m = m; }
  53. void seed(IntType x0) { _x = x0; }
  54. result_type operator()() { _x = (_a*_x+_c) % _m; return _x; }
  55. result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _c == 0 ? 1 : 0; }
  56. result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _m -1; }
  57. private:
  58. IntType _x, _a, _c, _m;
  59. };
  60. // simplest "random" number generator possible, to check on overhead
  61. class counting
  62. {
  63. public:
  64. typedef int result_type;
  65. BOOST_STATIC_CONSTANT(bool, has_fixed_range = false);
  66. counting() : _x(0) { }
  67. result_type operator()() { return ++_x; }
  68. result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 1; }
  69. result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return (std::numeric_limits<result_type>::max)(); }
  70. private:
  71. int _x;
  72. };
  73. // decoration of variate_generator to make it runtime-exchangeable
  74. // for speed comparison
  75. template<class Ret>
  76. class RandomGenBase
  77. {
  78. public:
  79. virtual Ret operator()() = 0;
  80. virtual ~RandomGenBase() { }
  81. };
  82. template<class URNG, class Dist, class Ret = typename Dist::result_type>
  83. class DynamicRandomGenerator
  84. : public RandomGenBase<Ret>
  85. {
  86. public:
  87. DynamicRandomGenerator(URNG& urng, const Dist& d) : _rng(urng, d) { }
  88. Ret operator()() { return _rng(); }
  89. private:
  90. boost::variate_generator<URNG&, Dist> _rng;
  91. };
  92. template<class Ret>
  93. class GenericRandomGenerator
  94. {
  95. public:
  96. typedef Ret result_type;
  97. GenericRandomGenerator() { };
  98. void set(boost::shared_ptr<RandomGenBase<Ret> > p) { _p = p; }
  99. // takes over ownership
  100. void set(RandomGenBase<Ret> * p) { _p.reset(p); }
  101. Ret operator()() { return (*_p)(); }
  102. private:
  103. boost::shared_ptr<RandomGenBase<Ret> > _p;
  104. };
  105. // start implementation of measuring timing
  106. void show_elapsed(double end, int iter, const std::string & name)
  107. {
  108. double usec = end/iter*1e6;
  109. double cycles = usec * cpu_frequency/1e6;
  110. std::cout << name << ": "
  111. << usec*1e3 << " nsec/loop = "
  112. << cycles << " CPU cycles"
  113. << std::endl;
  114. }
  115. #if 0
  116. template<class RNG>
  117. void timing(RNG & rng, int iter, const std::string& name)
  118. {
  119. // make sure we're not optimizing too much
  120. volatile typename RNG::result_type tmp;
  121. boost::timer t;
  122. for(int i = 0; i < iter; i++)
  123. tmp = rng();
  124. show_elapsed(t.elapsed(), iter, name);
  125. }
  126. #endif
  127. // overload for using a copy, allows more concise invocation
  128. template<class RNG>
  129. void timing(RNG rng, int iter, const std::string& name)
  130. {
  131. // make sure we're not optimizing too much
  132. volatile typename RNG::result_type tmp;
  133. boost::timer t;
  134. for(int i = 0; i < iter; i++)
  135. tmp = rng();
  136. show_elapsed(t.elapsed(), iter, name);
  137. }
  138. template<class RNG>
  139. void timing_sphere(RNG rng, int iter, const std::string & name)
  140. {
  141. boost::timer t;
  142. for(int i = 0; i < iter; i++) {
  143. // the special return value convention of uniform_on_sphere saves 20% CPU
  144. const std::vector<double> & tmp = rng();
  145. (void) tmp[0];
  146. }
  147. show_elapsed(t.elapsed(), iter, name);
  148. }
  149. template<class RNG>
  150. void run(int iter, const std::string & name, RNG rng)
  151. {
  152. std::cout << (RNG::has_fixed_range ? "fixed-range " : "");
  153. // BCC has trouble with string autoconversion for explicit specializations
  154. // make sure we're not optimizing too much
  155. volatile typename RNG::result_type tmp;
  156. boost::timer t;
  157. for(int i = 0; i < iter; i++)
  158. tmp = rng();
  159. show_elapsed(t.elapsed(), iter, name);
  160. }
  161. #ifdef HAVE_DRAND48
  162. // requires non-standard C library support for srand48/lrand48
  163. struct lrand48_ {
  164. static const bool has_fixed_range = false;
  165. typedef long result_type;
  166. lrand48_() {
  167. using namespace std;
  168. srand48(1);
  169. }
  170. result_type operator()() {
  171. using namespace std;
  172. return lrand48();
  173. }
  174. };
  175. #endif
  176. #ifdef HAVE_MT19937INT_C // requires the original mt19937int.c
  177. extern "C" void sgenrand(unsigned long);
  178. extern "C" unsigned long genrand();
  179. void run(int iter, const std::string & name, float)
  180. {
  181. sgenrand(4357);
  182. timing(genrand, iter, name, 0u);
  183. }
  184. #endif
  185. #ifdef HAVE_MT19937AR_C
  186. extern "C" {
  187. void init_genrand(unsigned long s);
  188. unsigned long genrand_int32(void);
  189. }
  190. struct mt19937_c {
  191. static const bool has_fixed_range = false;
  192. mt19937_c() {
  193. init_genrand(5489);
  194. }
  195. typedef unsigned long result_type;
  196. result_type operator()() {
  197. return genrand_int32();
  198. }
  199. };
  200. #endif
  201. template<class PRNG, class Dist>
  202. inline boost::variate_generator<PRNG&, Dist> make_gen(PRNG & rng, Dist d)
  203. {
  204. return boost::variate_generator<PRNG&, Dist>(rng, d);
  205. }
  206. template<class Gen>
  207. void distrib(int iter, const std::string & name, const Gen &)
  208. {
  209. Gen gen;
  210. timing(make_gen(gen, boost::random::uniform_int_distribution<>(-2, 4)),
  211. iter, name + " uniform_int");
  212. timing(make_gen(gen, boost::random::uniform_smallint<>(-2, 4)),
  213. iter, name + " uniform_smallint");
  214. timing(make_gen(gen, boost::random::bernoulli_distribution<>(0.5)),
  215. iter, name + " bernoulli");
  216. timing(make_gen(gen, boost::random::geometric_distribution<>(0.5)),
  217. iter, name + " geometric");
  218. timing(make_gen(gen, boost::random::binomial_distribution<int>(4, 0.8)),
  219. iter, name + " binomial");
  220. timing(make_gen(gen, boost::random::negative_binomial_distribution<int>(4, 0.8)),
  221. iter, name + " negative_binomial");
  222. timing(make_gen(gen, boost::random::poisson_distribution<>(1)),
  223. iter, name + " poisson");
  224. timing(make_gen(gen, boost::random::uniform_real_distribution<>(-5.3, 4.8)),
  225. iter, name + " uniform_real");
  226. timing(make_gen(gen, boost::random::uniform_01<>()),
  227. iter, name + " uniform_01");
  228. timing(make_gen(gen, boost::random::triangle_distribution<>(1, 2, 7)),
  229. iter, name + " triangle");
  230. timing(make_gen(gen, boost::random::exponential_distribution<>(3)),
  231. iter, name + " exponential");
  232. timing(make_gen(gen, boost::random::normal_distribution<>()),
  233. iter, name + " normal polar");
  234. timing(make_gen(gen, boost::random::lognormal_distribution<>()),
  235. iter, name + " lognormal");
  236. timing(make_gen(gen, boost::random::chi_squared_distribution<>(4)),
  237. iter, name + " chi squared");
  238. timing(make_gen(gen, boost::random::cauchy_distribution<>()),
  239. iter, name + " cauchy");
  240. timing(make_gen(gen, boost::random::fisher_f_distribution<>(4, 5)),
  241. iter, name + " fisher f");
  242. timing(make_gen(gen, boost::random::student_t_distribution<>(7)),
  243. iter, name + " student t");
  244. timing(make_gen(gen, boost::random::gamma_distribution<>(2.8)),
  245. iter, name + " gamma");
  246. timing(make_gen(gen, boost::random::weibull_distribution<>(3)),
  247. iter, name + " weibull");
  248. timing(make_gen(gen, boost::random::extreme_value_distribution<>()),
  249. iter, name + " extreme value");
  250. timing_sphere(make_gen(gen, boost::random::uniform_on_sphere<>(3)),
  251. iter/10, name + " uniform_on_sphere");
  252. }
  253. int main(int argc, char*argv[])
  254. {
  255. if(argc != 2) {
  256. std::cerr << "usage: " << argv[0] << " iterations" << std::endl;
  257. return 1;
  258. }
  259. // okay, it's ugly, but it's only used here
  260. int iter =
  261. #ifndef BOOST_NO_STDC_NAMESPACE
  262. std::
  263. #endif
  264. atoi(argv[1]);
  265. #if !defined(BOOST_NO_INT64_T) && \
  266. !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)
  267. run(iter, "rand48", boost::rand48());
  268. linear_congruential<boost::uint64_t>
  269. lcg48(boost::uint64_t(1)<<16 | 0x330e,
  270. boost::uint64_t(0xDEECE66DUL) | (boost::uint64_t(0x5) << 32), 0xB,
  271. boost::uint64_t(1)<<48);
  272. timing(lcg48, iter, "lrand48 run-time");
  273. #endif
  274. #ifdef HAVE_DRAND48
  275. // requires non-standard C library support for srand48/lrand48
  276. run(iter, "lrand48", lrand48_()); // coded for lrand48()
  277. #endif
  278. run(iter, "minstd_rand0", boost::minstd_rand0());
  279. run(iter, "minstd_rand", boost::minstd_rand());
  280. run(iter, "ecuyer combined", boost::ecuyer1988());
  281. run(iter, "kreutzer1986", boost::kreutzer1986());
  282. run(iter, "taus88", boost::taus88());
  283. run(iter, "knuth_b", boost::random::knuth_b());
  284. run(iter, "hellekalek1995 (inversive)", boost::hellekalek1995());
  285. run(iter, "mt11213b", boost::mt11213b());
  286. run(iter, "mt19937", boost::mt19937());
  287. #if !defined(BOOST_NO_INT64_T)
  288. run(iter, "mt19937_64", boost::mt19937_64());
  289. #endif
  290. run(iter, "lagged_fibonacci607", boost::lagged_fibonacci607());
  291. run(iter, "lagged_fibonacci1279", boost::lagged_fibonacci1279());
  292. run(iter, "lagged_fibonacci2281", boost::lagged_fibonacci2281());
  293. run(iter, "lagged_fibonacci3217", boost::lagged_fibonacci3217());
  294. run(iter, "lagged_fibonacci4423", boost::lagged_fibonacci4423());
  295. run(iter, "lagged_fibonacci9689", boost::lagged_fibonacci9689());
  296. run(iter, "lagged_fibonacci19937", boost::lagged_fibonacci19937());
  297. run(iter, "lagged_fibonacci23209", boost::lagged_fibonacci23209());
  298. run(iter, "lagged_fibonacci44497", boost::lagged_fibonacci44497());
  299. run(iter, "subtract_with_carry", boost::random::ranlux_base());
  300. run(iter, "subtract_with_carry_01", boost::random::ranlux_base_01());
  301. run(iter, "ranlux3", boost::ranlux3());
  302. run(iter, "ranlux4", boost::ranlux4());
  303. run(iter, "ranlux3_01", boost::ranlux3_01());
  304. run(iter, "ranlux4_01", boost::ranlux4_01());
  305. run(iter, "ranlux64_3", boost::ranlux3());
  306. run(iter, "ranlux64_4", boost::ranlux4());
  307. run(iter, "ranlux64_3_01", boost::ranlux3_01());
  308. run(iter, "ranlux64_4_01", boost::ranlux4_01());
  309. run(iter, "ranlux24", boost::ranlux3());
  310. run(iter, "ranlux48", boost::ranlux4());
  311. run(iter, "counting", counting());
  312. #ifdef HAVE_MT19937INT_C
  313. // requires the original mt19937int.c
  314. run<float>(iter, "mt19937 original"); // coded for sgenrand()/genrand()
  315. #endif
  316. #ifdef HAVE_MT19937AR_C
  317. run(iter, "mt19937ar.c", mt19937_c());
  318. #endif
  319. distrib(iter, "counting", counting());
  320. distrib(iter, "minstd_rand", boost::minstd_rand());
  321. distrib(iter, "kreutzer1986", boost::kreutzer1986());
  322. distrib(iter, "mt19937", boost::mt19937());
  323. distrib(iter, "lagged_fibonacci607", boost::lagged_fibonacci607());
  324. }