1 // 2 // Copyright (c) 2017, 2018 James E. King III 3 // 4 // Distributed under the Boost Software License, Version 1.0. 5 // (See accompanying file LICENSE_1_0.txt or copy at 6 // https://www.boost.org/LICENSE_1_0.txt) 7 // 8 // benchmark for random_generators in different forms 9 // 10 11 #include <boost/core/ignore_unused.hpp> 12 #include <boost/timer/timer.hpp> 13 #include <boost/predef/os.h> 14 #include <boost/uuid/random_generator.hpp> 15 #include <boost/uuid/uuid.hpp> 16 #include <boost/uuid/uuid_io.hpp> 17 #include <iostream> 18 #include <limits> 19 20 #if !defined(BOOST_NO_STRESS_TEST) 21 // must be a Valgrind, UBsan, or other stressful job 22 #define AVG_LOOPS 1 23 #define GEN_LOOPS 10 24 #define REUSE_LOOPS 100 25 #else 26 #define AVG_LOOPS 10 27 #define GEN_LOOPS 10000 28 #define REUSE_LOOPS 1000000 29 #endif 30 31 template<class Generator> auto_timed_generator_ctordtor(size_t count)32void auto_timed_generator_ctordtor(size_t count) 33 { 34 boost::timer::auto_cpu_timer t; 35 for (size_t i = 0; i < count; ++i) 36 { 37 Generator gen; 38 boost::ignore_unused(gen); 39 } 40 } 41 42 template<class Generator> auto_timed_generator_novel(size_t count)43void auto_timed_generator_novel(size_t count) 44 { 45 boost::timer::auto_cpu_timer t; 46 for (size_t i = 0; i < count; ++i) 47 { 48 Generator gen; 49 boost::uuids::uuid u = gen(); 50 boost::ignore_unused(u); 51 } 52 } 53 54 template<class Generator> auto_timed_generator_reuse(size_t count)55void auto_timed_generator_reuse(size_t count) 56 { 57 Generator gen; 58 { 59 boost::timer::auto_cpu_timer t; 60 for (size_t i = 0; i < count; ++i) 61 { 62 boost::uuids::uuid u = gen(); 63 boost::ignore_unused(u); 64 } 65 } 66 } 67 68 template<class Generator> timed_generator(size_t count)69boost::timer::cpu_times timed_generator(size_t count) 70 { 71 boost::timer::cpu_timer t; 72 Generator gen; 73 for (size_t i = 0; i < count; ++i) 74 { 75 boost::uuids::uuid u = gen(); 76 boost::ignore_unused(u); 77 } 78 return t.elapsed(); 79 } 80 main(int,char * [])81int main(int, char*[]) 82 { 83 std::cout << "Operating system entropy provider: " 84 << boost::uuids::detail::random_provider().name() << std::endl; 85 86 #if !defined(BOOST_NO_STRESS_TEST) 87 88 // 89 // Determine the cutoff point where it is more wall-clock efficient to 90 // use the bulk generator over the standard one. 91 // 92 93 std::cout << "Calculating the number of operator() calls where random_generator" << std::endl; 94 std::cout << "is more efficient than random_generator_mt19937..." << std::endl; 95 std::cout << "at "; 96 bool asterisk = false; 97 size_t minn = (std::numeric_limits<size_t>::max)(); 98 size_t summ = 0; 99 size_t maxx = 0; 100 for (size_t i = 0; i < AVG_LOOPS + 1; ++i) // the first loop is thrown away, see below 101 { 102 size_t answer = 0; 103 for (size_t count = 1; !answer; ++count) 104 { 105 boost::timer::cpu_times standard = timed_generator<boost::uuids::random_generator>(count); 106 boost::timer::cpu_times pseudo = timed_generator<boost::uuids::random_generator_mt19937>(count); 107 if (standard.wall > pseudo.wall) 108 { 109 answer = count; 110 } 111 else if (count >= 999) 112 { 113 std::cout << "*"; 114 asterisk = true; 115 answer = count; 116 } 117 } 118 119 // throw away the first answer in case it contains time related to loading 120 // or initializing the crypto library being used 121 if (i > 0) 122 { 123 if (minn > answer) minn = answer; 124 if (maxx < answer) maxx = answer; 125 summ += answer; 126 std::cout << answer << " " << std::flush; 127 } 128 } 129 if (asterisk) 130 { 131 std::cout << "* = limited to 999" << std::endl; 132 } 133 std::cout << "calls to operator()" << std::endl; 134 size_t answer = summ / AVG_LOOPS; 135 std::cout << "For this platform, random_generator_mt19937 outperforms " 136 << "random_generator after " << answer << " generations (min " << minn << " / max " << maxx << ")." 137 << std::endl; 138 std::cout << std::endl; 139 140 #endif 141 142 // 143 // Measure ctor/dtor of both 144 // 145 std::cout << "Construction/destruction time for random_generator " 146 << "(" << GEN_LOOPS << " iterations): " << std::endl; 147 auto_timed_generator_ctordtor<boost::uuids::random_generator>(GEN_LOOPS); 148 std::cout << std::endl; 149 150 std::cout << "Construction/destruction time for random_generator_mt19937 " 151 << "(" << GEN_LOOPS << " iterations): " << std::endl; 152 auto_timed_generator_ctordtor<boost::uuids::random_generator_mt19937>(GEN_LOOPS); 153 std::cout << std::endl; 154 155 // 156 // Two common use cases: 157 // 158 // Use an OS provided RNG which has no seed code but is slower to reuse 159 // Use a PRNG which is expensive to seed once but fast to reuse 160 // 161 // Measure the default selections of the library 162 // 163 164 std::cout << "Benchmark boost::uuids::random_generator " 165 << "(reused for " << REUSE_LOOPS << " loops):" << std::endl; 166 auto_timed_generator_reuse<boost::uuids::random_generator>(REUSE_LOOPS); 167 std::cout << std::endl; 168 169 std::cout << "Benchmark boost::uuids::random_generator_mt19937 " 170 << "(reused for " << REUSE_LOOPS << " loops):" << std::endl; 171 auto_timed_generator_reuse<boost::uuids::random_generator_mt19937>(REUSE_LOOPS); 172 std::cout << std::endl; 173 174 std::cout << "Benchmark boost::uuids::random_generator " 175 << "(new generator each loop for " << GEN_LOOPS << " loops):" << std::endl; 176 auto_timed_generator_novel<boost::uuids::random_generator>(GEN_LOOPS); 177 std::cout << std::endl; 178 179 std::cout << "Benchmark boost::uuids::random_generator_mt19937 " 180 << "(new generator each loop for " << GEN_LOOPS << " loops):" << std::endl; 181 auto_timed_generator_novel<boost::uuids::random_generator_mt19937>(GEN_LOOPS); 182 std::cout << std::endl; 183 184 return 0; 185 } 186 187 188