/* boost random_speed.cpp performance measurements * * Copyright Jens Maurer 2000 * Distributed under the Boost Software License, Version 1.0. (See * accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * $Id$ */ #include #include #include #include #include #include #include /* * Configuration Section */ // define if your C library supports the non-standard drand48 family //#define HAVE_DRAND48 // define if you have the original mt19937int.c (with commented out main()) #undef HAVE_MT19937INT_C // define if you have the original mt19937ar.c (with commented out main()) // #define HAVE_MT19937AR_C // set to your CPU frequency static const double cpu_frequency = 1.87 * 1e9; /* * End of Configuration Section */ /* * General portability note: * MSVC mis-compiles explicit function template instantiations. * For example, f() and f() are both compiled to call f(). * BCC is unable to implicitly convert a "const char *" to a std::string * when using explicit function template instantiations. * * Therefore, avoid explicit function template instantiations. */ // provides a run-time configurable linear congruential generator, just // for comparison template class linear_congruential { public: typedef IntType result_type; BOOST_STATIC_CONSTANT(bool, has_fixed_range = false); linear_congruential(IntType x0, IntType a, IntType c, IntType m) : _x(x0), _a(a), _c(c), _m(m) { } // compiler-generated copy ctor and assignment operator are fine void seed(IntType x0, IntType a, IntType c, IntType m) { _x = x0; _a = a; _c = c; _m = m; } void seed(IntType x0) { _x = x0; } result_type operator()() { _x = (_a*_x+_c) % _m; return _x; } result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _c == 0 ? 1 : 0; } result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _m -1; } private: IntType _x, _a, _c, _m; }; // simplest "random" number generator possible, to check on overhead class counting { public: typedef int result_type; BOOST_STATIC_CONSTANT(bool, has_fixed_range = false); counting() : _x(0) { } result_type operator()() { return ++_x; } result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 1; } result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return (std::numeric_limits::max)(); } private: int _x; }; // decoration of variate_generator to make it runtime-exchangeable // for speed comparison template class RandomGenBase { public: virtual Ret operator()() = 0; virtual ~RandomGenBase() { } }; template class DynamicRandomGenerator : public RandomGenBase { public: DynamicRandomGenerator(URNG& urng, const Dist& d) : _rng(urng, d) { } Ret operator()() { return _rng(); } private: boost::variate_generator _rng; }; template class GenericRandomGenerator { public: typedef Ret result_type; GenericRandomGenerator() { }; void set(boost::shared_ptr > p) { _p = p; } // takes over ownership void set(RandomGenBase * p) { _p.reset(p); } Ret operator()() { return (*_p)(); } private: boost::shared_ptr > _p; }; // start implementation of measuring timing void show_elapsed(double end, int iter, const std::string & name) { double usec = end/iter*1e6; double cycles = usec * cpu_frequency/1e6; std::cout << name << ": " << usec*1e3 << " nsec/loop = " << cycles << " CPU cycles" << std::endl; } #if 0 template void timing(RNG & rng, int iter, const std::string& name) { // make sure we're not optimizing too much volatile typename RNG::result_type tmp; boost::timer t; for(int i = 0; i < iter; i++) tmp = rng(); show_elapsed(t.elapsed(), iter, name); } #endif // overload for using a copy, allows more concise invocation template void timing(RNG rng, int iter, const std::string& name) { // make sure we're not optimizing too much volatile typename RNG::result_type tmp; boost::timer t; for(int i = 0; i < iter; i++) tmp = rng(); show_elapsed(t.elapsed(), iter, name); } template void timing_sphere(RNG rng, int iter, const std::string & name) { boost::timer t; for(int i = 0; i < iter; i++) { // the special return value convention of uniform_on_sphere saves 20% CPU const std::vector & tmp = rng(); (void) tmp[0]; } show_elapsed(t.elapsed(), iter, name); } template void run(int iter, const std::string & name, RNG rng) { std::cout << (RNG::has_fixed_range ? "fixed-range " : ""); // BCC has trouble with string autoconversion for explicit specializations // make sure we're not optimizing too much volatile typename RNG::result_type tmp; boost::timer t; for(int i = 0; i < iter; i++) tmp = rng(); show_elapsed(t.elapsed(), iter, name); } #ifdef HAVE_DRAND48 // requires non-standard C library support for srand48/lrand48 struct lrand48_ { static const bool has_fixed_range = false; typedef long result_type; lrand48_() { using namespace std; srand48(1); } result_type operator()() { using namespace std; return lrand48(); } }; #endif #ifdef HAVE_MT19937INT_C // requires the original mt19937int.c extern "C" void sgenrand(unsigned long); extern "C" unsigned long genrand(); void run(int iter, const std::string & name, float) { sgenrand(4357); timing(genrand, iter, name, 0u); } #endif #ifdef HAVE_MT19937AR_C extern "C" { void init_genrand(unsigned long s); unsigned long genrand_int32(void); } struct mt19937_c { static const bool has_fixed_range = false; mt19937_c() { init_genrand(5489); } typedef unsigned long result_type; result_type operator()() { return genrand_int32(); } }; #endif template inline boost::variate_generator make_gen(PRNG & rng, Dist d) { return boost::variate_generator(rng, d); } template void distrib(int iter, const std::string & name, const Gen &) { Gen gen; timing(make_gen(gen, boost::random::uniform_int_distribution<>(-2, 4)), iter, name + " uniform_int"); timing(make_gen(gen, boost::random::uniform_smallint<>(-2, 4)), iter, name + " uniform_smallint"); timing(make_gen(gen, boost::random::bernoulli_distribution<>(0.5)), iter, name + " bernoulli"); timing(make_gen(gen, boost::random::geometric_distribution<>(0.5)), iter, name + " geometric"); timing(make_gen(gen, boost::random::binomial_distribution(4, 0.8)), iter, name + " binomial"); timing(make_gen(gen, boost::random::negative_binomial_distribution(4, 0.8)), iter, name + " negative_binomial"); timing(make_gen(gen, boost::random::poisson_distribution<>(1)), iter, name + " poisson"); timing(make_gen(gen, boost::random::uniform_real_distribution<>(-5.3, 4.8)), iter, name + " uniform_real"); timing(make_gen(gen, boost::random::uniform_01<>()), iter, name + " uniform_01"); timing(make_gen(gen, boost::random::triangle_distribution<>(1, 2, 7)), iter, name + " triangle"); timing(make_gen(gen, boost::random::exponential_distribution<>(3)), iter, name + " exponential"); timing(make_gen(gen, boost::random::normal_distribution<>()), iter, name + " normal polar"); timing(make_gen(gen, boost::random::lognormal_distribution<>()), iter, name + " lognormal"); timing(make_gen(gen, boost::random::chi_squared_distribution<>(4)), iter, name + " chi squared"); timing(make_gen(gen, boost::random::cauchy_distribution<>()), iter, name + " cauchy"); timing(make_gen(gen, boost::random::fisher_f_distribution<>(4, 5)), iter, name + " fisher f"); timing(make_gen(gen, boost::random::student_t_distribution<>(7)), iter, name + " student t"); timing(make_gen(gen, boost::random::gamma_distribution<>(2.8)), iter, name + " gamma"); timing(make_gen(gen, boost::random::weibull_distribution<>(3)), iter, name + " weibull"); timing(make_gen(gen, boost::random::extreme_value_distribution<>()), iter, name + " extreme value"); timing_sphere(make_gen(gen, boost::random::uniform_on_sphere<>(3)), iter/10, name + " uniform_on_sphere"); } int main(int argc, char*argv[]) { if(argc != 2) { std::cerr << "usage: " << argv[0] << " iterations" << std::endl; return 1; } // okay, it's ugly, but it's only used here int iter = #ifndef BOOST_NO_STDC_NAMESPACE std:: #endif atoi(argv[1]); #if !defined(BOOST_NO_INT64_T) && \ !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) run(iter, "rand48", boost::rand48()); linear_congruential lcg48(boost::uint64_t(1)<<16 | 0x330e, boost::uint64_t(0xDEECE66DUL) | (boost::uint64_t(0x5) << 32), 0xB, boost::uint64_t(1)<<48); timing(lcg48, iter, "lrand48 run-time"); #endif #ifdef HAVE_DRAND48 // requires non-standard C library support for srand48/lrand48 run(iter, "lrand48", lrand48_()); // coded for lrand48() #endif run(iter, "minstd_rand0", boost::minstd_rand0()); run(iter, "minstd_rand", boost::minstd_rand()); run(iter, "ecuyer combined", boost::ecuyer1988()); run(iter, "kreutzer1986", boost::kreutzer1986()); run(iter, "taus88", boost::taus88()); run(iter, "knuth_b", boost::random::knuth_b()); run(iter, "hellekalek1995 (inversive)", boost::hellekalek1995()); run(iter, "mt11213b", boost::mt11213b()); run(iter, "mt19937", boost::mt19937()); #if !defined(BOOST_NO_INT64_T) run(iter, "mt19937_64", boost::mt19937_64()); #endif run(iter, "lagged_fibonacci607", boost::lagged_fibonacci607()); run(iter, "lagged_fibonacci1279", boost::lagged_fibonacci1279()); run(iter, "lagged_fibonacci2281", boost::lagged_fibonacci2281()); run(iter, "lagged_fibonacci3217", boost::lagged_fibonacci3217()); run(iter, "lagged_fibonacci4423", boost::lagged_fibonacci4423()); run(iter, "lagged_fibonacci9689", boost::lagged_fibonacci9689()); run(iter, "lagged_fibonacci19937", boost::lagged_fibonacci19937()); run(iter, "lagged_fibonacci23209", boost::lagged_fibonacci23209()); run(iter, "lagged_fibonacci44497", boost::lagged_fibonacci44497()); run(iter, "subtract_with_carry", boost::random::ranlux_base()); run(iter, "subtract_with_carry_01", boost::random::ranlux_base_01()); run(iter, "ranlux3", boost::ranlux3()); run(iter, "ranlux4", boost::ranlux4()); run(iter, "ranlux3_01", boost::ranlux3_01()); run(iter, "ranlux4_01", boost::ranlux4_01()); run(iter, "ranlux64_3", boost::ranlux3()); run(iter, "ranlux64_4", boost::ranlux4()); run(iter, "ranlux64_3_01", boost::ranlux3_01()); run(iter, "ranlux64_4_01", boost::ranlux4_01()); run(iter, "ranlux24", boost::ranlux3()); run(iter, "ranlux48", boost::ranlux4()); run(iter, "counting", counting()); #ifdef HAVE_MT19937INT_C // requires the original mt19937int.c run(iter, "mt19937 original"); // coded for sgenrand()/genrand() #endif #ifdef HAVE_MT19937AR_C run(iter, "mt19937ar.c", mt19937_c()); #endif distrib(iter, "counting", counting()); distrib(iter, "minstd_rand", boost::minstd_rand()); distrib(iter, "kreutzer1986", boost::kreutzer1986()); distrib(iter, "mt19937", boost::mt19937()); distrib(iter, "lagged_fibonacci607", boost::lagged_fibonacci607()); }