1 /* boost random_speed.cpp performance measurements
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)
14 #include <boost/config.hpp>
15 #include <boost/random.hpp>
16 #include <boost/progress.hpp>
17 #include <boost/shared_ptr.hpp>
20 * Configuration Section
23 // define if your C library supports the non-standard drand48 family
26 // define if you have the original mt19937int.c (with commented out main())
27 #undef HAVE_MT19937INT_C
29 // set to your CPU frequency in MHz
30 static const double cpu_frequency
= 200 * 1e6
;
33 * End of Configuration Section
37 * General portability note:
38 * MSVC mis-compiles explicit function template instantiations.
39 * For example, f<A>() and f<B>() are both compiled to call f<A>().
40 * BCC is unable to implicitly convert a "const char *" to a std::string
41 * when using explicit function template instantiations.
43 * Therefore, avoid explicit function template instantiations.
46 // provides a run-time configurable linear congruential generator, just
48 template<class IntType
>
49 class linear_congruential
52 typedef IntType result_type
;
54 BOOST_STATIC_CONSTANT(bool, has_fixed_range
= false);
56 linear_congruential(IntType x0
, IntType a
, IntType c
, IntType m
)
57 : _x(x0
), _a(a
), _c(c
), _m(m
) { }
58 // compiler-generated copy ctor and assignment operator are fine
59 void seed(IntType x0
, IntType a
, IntType c
, IntType m
)
60 { _x
= x0
; _a
= a
; _c
= c
; _m
= m
; }
61 void seed(IntType x0
) { _x
= x0
; }
62 result_type
operator()() { _x
= (_a
*_x
+_c
) % _m
; return _x
; }
63 result_type min
BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _c
== 0 ? 1 : 0; }
64 result_type max
BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _m
-1; }
67 IntType _x
, _a
, _c
, _m
;
71 // simplest "random" number generator possible, to check on overhead
75 typedef int result_type
;
77 BOOST_STATIC_CONSTANT(bool, has_fixed_range
= false);
79 counting() : _x(0) { }
80 result_type
operator()() { return ++_x
; }
81 result_type min
BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 1; }
82 result_type max
BOOST_PREVENT_MACRO_SUBSTITUTION () const { return (std::numeric_limits
<result_type
>::max
)(); }
89 // decoration of variate_generator to make it runtime-exchangeable
90 // for speed comparison
95 virtual Ret
operator()() = 0;
96 virtual ~RandomGenBase() { }
99 template<class URNG
, class Dist
, class Ret
= typename
Dist::result_type
>
100 class DynamicRandomGenerator
101 : public RandomGenBase
<Ret
>
104 DynamicRandomGenerator(URNG
& urng
, const Dist
& d
) : _rng(urng
, d
) { }
105 Ret
operator()() { return _rng(); }
107 boost::variate_generator
<URNG
&, Dist
> _rng
;
111 class GenericRandomGenerator
114 typedef Ret result_type
;
116 GenericRandomGenerator() { };
117 void set(boost::shared_ptr
<RandomGenBase
<Ret
> > p
) { _p
= p
; }
118 // takes over ownership
119 void set(RandomGenBase
<Ret
> * p
) { _p
.reset(p
); }
120 Ret
operator()() { return (*_p
)(); }
122 boost::shared_ptr
<RandomGenBase
<Ret
> > _p
;
126 // start implementation of measuring timing
128 void show_elapsed(double end
, int iter
, const std::string
& name
)
130 double usec
= end
/iter
*1e6
;
131 double cycles
= usec
* cpu_frequency
/1e6
;
132 std::cout
<< name
<< ": "
133 << usec
*1e3
<< " nsec/loop = "
134 << cycles
<< " CPU cycles"
140 void timing(RNG
& rng
, int iter
, const std::string
& name
)
142 // make sure we're not optimizing too much
143 volatile typename
RNG::result_type tmp
;
145 for(int i
= 0; i
< iter
; i
++)
147 show_elapsed(t
.elapsed(), iter
, name
);
151 // overload for using a copy, allows more concise invocation
153 void timing(RNG rng
, int iter
, const std::string
& name
)
155 // make sure we're not optimizing too much
156 volatile typename
RNG::result_type tmp
;
158 for(int i
= 0; i
< iter
; i
++)
160 show_elapsed(t
.elapsed(), iter
, name
);
164 void timing_sphere(RNG rng
, int iter
, const std::string
& name
)
167 for(int i
= 0; i
< iter
; i
++) {
168 // the special return value convention of uniform_on_sphere saves 20% CPU
169 const std::vector
<double> & tmp
= rng();
172 show_elapsed(t
.elapsed(), iter
, name
);
176 void run(int iter
, const std::string
& name
, const RNG
&)
178 std::cout
<< (RNG::has_fixed_range
? "fixed-range " : "");
179 // BCC has trouble with string autoconversion for explicit specializations
180 timing(RNG(), iter
, std::string(name
));
184 // requires non-standard C library support for srand48/lrand48
185 void run(int iter
, const std::string
& name
, int)
188 timing(&std::lrand48
, iter
, name
);
192 #ifdef HAVE_MT19937INT_C // requires the original mt19937int.c
193 extern "C" void sgenrand(unsigned long);
194 extern "C" unsigned long genrand();
196 void run(int iter
, const std::string
& name
, float)
199 timing(genrand
, iter
, name
, 0u);
203 template<class PRNG
, class Dist
>
204 inline boost::variate_generator
<PRNG
&, Dist
> make_gen(PRNG
& rng
, Dist d
)
206 return boost::variate_generator
<PRNG
&, Dist
>(rng
, d
);
210 void distrib(int iter
, const std::string
& name
, const Gen
&)
214 timing(make_gen(gen
, boost::uniform_int
<>(-2, 4)),
215 iter
, name
+ " uniform_int");
217 timing(make_gen(gen
, boost::geometric_distribution
<>(0.5)),
218 iter
, name
+ " geometric");
220 timing(make_gen(gen
, boost::binomial_distribution
<int>(4, 0.8)),
221 iter
, name
+ " binomial");
223 timing(make_gen(gen
, boost::poisson_distribution
<>(1)),
224 iter
, name
+ " poisson");
227 timing(make_gen(gen
, boost::uniform_real
<>(-5.3, 4.8)),
228 iter
, name
+ " uniform_real");
230 timing(make_gen(gen
, boost::triangle_distribution
<>(1, 2, 7)),
231 iter
, name
+ " triangle");
233 timing(make_gen(gen
, boost::exponential_distribution
<>(3)),
234 iter
, name
+ " exponential");
236 timing(make_gen(gen
, boost::normal_distribution
<>()),
237 iter
, name
+ " normal polar");
239 timing(make_gen(gen
, boost::lognormal_distribution
<>()),
240 iter
, name
+ " lognormal");
242 timing(make_gen(gen
, boost::cauchy_distribution
<>()),
243 iter
, name
+ " cauchy");
245 timing(make_gen(gen
, boost::cauchy_distribution
<>()),
246 iter
, name
+ " gamma");
248 timing_sphere(make_gen(gen
, boost::uniform_on_sphere
<>(3)),
249 iter
/10, name
+ " uniform_on_sphere");
253 template<class URNG
, class Dist
>
254 inline boost::shared_ptr
<DynamicRandomGenerator
<URNG
, Dist
> >
255 make_dynamic(URNG
& rng
, const Dist
& d
)
257 typedef DynamicRandomGenerator
<URNG
, Dist
> type
;
258 return boost::shared_ptr
<type
>(new type(rng
, d
));
262 void distrib_runtime(int iter
, const std::string
& n
, const Gen
&)
264 std::string name
= n
+ " virtual function ";
267 GenericRandomGenerator
<int> g_int
;
269 g_int
.set(make_dynamic(gen
, boost::uniform_int
<>(-2,4)));
270 timing(g_int
, iter
, name
+ "uniform_int");
272 g_int
.set(make_dynamic(gen
, boost::geometric_distribution
<>(0.5)));
273 timing(g_int
, iter
, name
+ "geometric");
275 g_int
.set(make_dynamic(gen
, boost::binomial_distribution
<>(4, 0.8)));
276 timing(g_int
, iter
, name
+ "binomial");
278 g_int
.set(make_dynamic(gen
, boost::poisson_distribution
<>(1)));
279 timing(g_int
, iter
, name
+ "poisson");
281 GenericRandomGenerator
<double> g
;
283 g
.set(make_dynamic(gen
, boost::uniform_real
<>(-5.3, 4.8)));
284 timing(g
, iter
, name
+ "uniform_real");
286 g
.set(make_dynamic(gen
, boost::triangle_distribution
<>(1, 2, 7)));
287 timing(g
, iter
, name
+ "triangle");
289 g
.set(make_dynamic(gen
, boost::exponential_distribution
<>(3)));
290 timing(g
, iter
, name
+ "exponential");
292 g
.set(make_dynamic(gen
, boost::normal_distribution
<>()));
293 timing(g
, iter
, name
+ "normal polar");
295 g
.set(make_dynamic(gen
, boost::lognormal_distribution
<>()));
296 timing(g
, iter
, name
+ "lognormal");
298 g
.set(make_dynamic(gen
, boost::cauchy_distribution
<>()));
299 timing(g
, iter
, name
+ "cauchy");
301 g
.set(make_dynamic(gen
, boost::gamma_distribution
<>(0.4)));
302 timing(g
, iter
, name
+ "gamma");
306 int main(int argc
, char*argv
[])
309 std::cerr
<< "usage: " << argv
[0] << " iterations" << std::endl
;
313 // okay, it's ugly, but it's only used here
315 #ifndef BOOST_NO_STDC_NAMESPACE
320 #if !defined(BOOST_NO_INT64_T) && \
321 !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)
322 run(iter
, "rand48", boost::rand48());
323 linear_congruential
<boost::uint64_t>
324 lcg48(boost::uint64_t(1)<<16 | 0x330e,
325 boost::uint64_t(0xDEECE66DUL
) | (boost::uint64_t(0x5) << 32), 0xB,
326 boost::uint64_t(1)<<48);
327 timing(lcg48
, iter
, "lrand48 run-time");
331 // requires non-standard C library support for srand48/lrand48
332 run(iter
, "lrand48", 0); // coded for lrand48()
335 run(iter
, "minstd_rand", boost::minstd_rand());
336 run(iter
, "ecuyer combined", boost::ecuyer1988());
337 run(iter
, "kreutzer1986", boost::kreutzer1986());
339 run(iter
, "hellekalek1995 (inversive)", boost::hellekalek1995());
341 run(iter
, "mt11213b", boost::mt11213b());
342 run(iter
, "mt19937", boost::mt19937());
344 run(iter
, "subtract_with_carry", boost::random::ranlux_base());
345 run(iter
, "subtract_with_carry_01", boost::random::ranlux_base_01());
346 run(iter
, "ranlux3", boost::ranlux3());
347 run(iter
, "ranlux4", boost::ranlux4());
348 run(iter
, "ranlux3_01", boost::ranlux3_01());
349 run(iter
, "ranlux4_01", boost::ranlux4_01());
350 run(iter
, "counting", counting());
352 #ifdef HAVE_MT19937INT_C
353 // requires the original mt19937int.c
354 run
<float>(iter
, "mt19937 original"); // coded for sgenrand()/genrand()
357 distrib(iter
, "counting", counting());
358 distrib_runtime(iter
, "counting", counting());
360 distrib(iter
, "minstd_rand", boost::minstd_rand());
362 distrib(iter
, "kreutzer1986", boost::kreutzer1986());
364 distrib(iter
, "mt19937", boost::mt19937());
365 distrib_runtime(iter
, "mt19937", boost::mt19937());