JayBeams  0.1
Another project to have fun coding.
initialize_mersenne_twister.hpp
Go to the documentation of this file.
1 #ifndef jb_testing_initialize_mersenne_twister_hpp
2 #define jb_testing_initialize_mersenne_twister_hpp
3 
4 #include <algorithm>
5 #include <random>
6 #include <string>
7 
8 namespace jb {
9 namespace testing {
10 
11 char const default_initialization_marker[] = "__default__";
12 
13 /**
14  * Initialize a Mersenne-Twister based generator.
15  *
16  * Often tests need to initialize a PRNG based on either a
17  * command-line argument (to make tests reproduceable), or from the
18  * std::random_device (to have good distribution properties in the
19  * PRGN). This is a fairly tedious initialization, so we have
20  * refactored it to a common function.
21  *
22  * @param seed the command-line argument seed, 0 means initialize from
23  * std::random_device.
24  * @param token the parameter to initialize std::random_device, if
25  * __default__ then the random device is default initialized. The
26  * semantics of @a token are platform specific, but on Linux it is
27  * usually the device to read random numbers from (/dev/urandom or
28  * /dev/random). It is very rare to need /dev/random, specially in
29  * tests, where this function is most commonly used.
30  * @return a Generator initialized as described above.
31  * @throws std::exception if the token is invalid on the platform.
32  *
33  * @tparam Generator an instantiation of
34  * std::mersenne_twister_generator<>, most likely std::mt19937 or
35  * std::mt19937_64.
36  */
37 template <typename Generator>
38 Generator initialize_mersenne_twister(int seed, std::string const& token) {
39  // ... the operations are generated based on a PRNG, we use one of
40  // the standard generators from the C++ library ...
41  if (seed != 0) {
42  // ... the user wants a repeatable (but not well randomized) test,
43  // this is useful when comparing micro-optimizations to see if
44  // anything has changed, or when measuring the performance of the
45  // microbenchmark framework itself ...
46  //
47  // BTW, I am aware (see [1]) of the deficiencies in initializing a
48  // Mersenne-Twister with only 32 bits of seed, in this case I am
49  // not looking for a statistically unbiased, nor a
50  // cryptographically strong PRNG. We just want something that can
51  // be easily controlled from the command line....
52  //
53  // [1]: http://www.pcg-random.org/posts/cpp-seeding-surprises.html
54  //
55  return Generator(seed);
56  }
57 
58  // ... in this case we make every effort to initialize from a good
59  // source of entropy ...
60 
61  // First, we need to know how much entropy will be needed by the
62  // Generator.
63  // A Mersenne-Twister generator will call the SeedSeq generate()
64  // function N*K times, where:
65  // N = generator.state_size
66  // K = ceil(generator.word_size / 32)
67  // details in:
68  // http://en.cppreference.com/w/cpp/numeric/random/mersenne_twister_engine
69  // so we populate the SeedSeq with that many words, tedious as
70  // that is.
71  //
72  // [2]: http://www.pcg-random.org/posts/cpps-random_device.html
73 
74  // ... compute how many words we need ...
75  auto const S = Generator::state_size * (Generator::word_size / 32);
76  // ... create a vector to hold the entropy, or what we believe is
77  // entropy ...
78  std::vector<unsigned int> entropy(S);
79  if (token == default_initialization_marker) {
80  // ... notice that the C++ standard does not guarantee that
81  // std::random_device produces actual random numbers, but with the
82  // compilers and libraries that JayBeams is compiled against there
83  // is good reason [3] to believe they are ...
84  //
85  // [3]:
86  // http://en.cppreference.com/w/cpp/numeric/random/random_device/random_device
87  //
88  std::random_device rd;
89  std::generate(entropy.begin(), entropy.end(), [&rd]() { return rd(); });
90  } else {
91  std::random_device rd(token);
92  std::generate(entropy.begin(), entropy.end(), [&rd]() { return rd(); });
93  }
94 
95  // ... std::seed_seq is an implementation of the SeedSeq concept,
96  // which cleverly remixes its input in case we got numbers in a
97  // small range. It will do complicated math to mix the input
98  // and ensure all the bits are distributed across as much of the
99  // space as possible, even with a single word of input ...
100  std::seed_seq seq(entropy.begin(), entropy.end());
101  return Generator(seq);
102 }
103 
104 } // namespace testing
105 } // namespace jb
106 
107 #endif // jb_testing_initialize_mersenne_twister_hpp
Generator initialize_mersenne_twister(int seed, std::string const &token)
Initialize a Mersenne-Twister based generator.
char const default_initialization_marker[]
The top-level namespace for the JayBeams library.
Definition: as_hhmmss.hpp:7