File: random_speed.cpp

package info (click to toggle)
boost1.83 1.83.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 545,632 kB
  • sloc: cpp: 3,857,086; xml: 125,552; ansic: 34,414; python: 25,887; asm: 5,276; sh: 4,799; ada: 1,681; makefile: 1,629; perl: 1,212; pascal: 1,139; sql: 810; yacc: 478; ruby: 102; lisp: 24; csh: 6
file content (403 lines) | stat: -rw-r--r-- 11,823 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/* 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 <iostream>
#include <cstdlib>
#include <string>
#include <boost/config.hpp>
#include <boost/random.hpp>
#include <boost/progress.hpp>
#include <boost/shared_ptr.hpp>

/*
 * 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<A>() and f<B>() are both compiled to call f<A>().
 * 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 IntType>
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<result_type>::max)(); }

private:
  int _x;
};


// decoration of variate_generator to make it runtime-exchangeable
// for speed comparison
template<class Ret>
class RandomGenBase
{
public:
  virtual Ret operator()() = 0;
  virtual ~RandomGenBase() { }
};

template<class URNG, class Dist, class Ret = typename Dist::result_type>
class DynamicRandomGenerator
  : public RandomGenBase<Ret>
{
public:
  DynamicRandomGenerator(URNG& urng, const Dist& d) : _rng(urng, d) { }
  Ret operator()() { return _rng(); }
private:
  boost::variate_generator<URNG&, Dist> _rng;
};

template<class Ret>
class GenericRandomGenerator
{
public:
  typedef Ret result_type;

  GenericRandomGenerator() { };
  void set(boost::shared_ptr<RandomGenBase<Ret> > p) { _p = p; }
  // takes over ownership
  void set(RandomGenBase<Ret> * p) { _p.reset(p); }
  Ret operator()() { return (*_p)(); }
private:
  boost::shared_ptr<RandomGenBase<Ret> > _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<class RNG>
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<class RNG>
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<class RNG>
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<double> & tmp = rng();
    (void) tmp[0];
  }
  show_elapsed(t.elapsed(), iter, name);
}

template<class RNG>
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<class PRNG, class Dist>
inline boost::variate_generator<PRNG&, Dist> make_gen(PRNG & rng, Dist d)
{
  return boost::variate_generator<PRNG&, Dist>(rng, d);
}

template<class Gen>
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<int>(4, 0.8)),
         iter, name + " binomial");

  timing(make_gen(gen, boost::random::negative_binomial_distribution<int>(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<boost::uint64_t>
    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, "mixmax", boost::random::mixmax());

  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<float>(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, "mixmax", boost::random::mixmax());
  
  distrib(iter, "lagged_fibonacci607", boost::lagged_fibonacci607());
}