├── README.md ├── bounded32.cpp ├── bounded64.cpp ├── download-gists.sh ├── gen-makefile.sh ├── gen-summary-tsv.sh ├── gen-tests.sh ├── remove-gists.sh ├── schemes-32.dat ├── schemes-64.dat ├── summarize.pl └── timer.hpp /README.md: -------------------------------------------------------------------------------- 1 | # Methods and Benchmarks for Random Numbers in a Range 2 | 3 | Implements most known methods for random numbers in a range, and provides 4 | some simple benchmarks. 5 | 6 | See http://www.pcg-random.org/posts/bounded-rands.html 7 | 8 | ## Building 9 | 10 | Run 11 | 12 | sh download-gists.sh 13 | sh gen-makefile.sh 14 | make -j 6 15 | 16 | ## Running all tests 17 | 18 | sh gen-tests.sh 19 | make -f Makefile.test -j 3 20 | sh gen-summary-tsv.sh 21 | 22 | -------------------------------------------------------------------------------- /bounded32.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * A C++ implementation methods and benchmarks for random numbers in a range 3 | * (32-bit version) 4 | * 5 | * The MIT License (MIT) 6 | * 7 | * Copyright (c) 2018 Melissa E. O'Neill 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a 10 | * copy of this software and associated documentation files (the "Software"), 11 | * to deal in the Software without restriction, including without limitation 12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 | * and/or sell copies of the Software, and to permit persons to whom the 14 | * Software is furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | * DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "pcg_random.hpp" 34 | #include "timer.hpp" 35 | 36 | #ifdef RNG_INCLUDE 37 | #include RNG_INCLUDE 38 | #endif 39 | 40 | #ifndef RNG_TYPE 41 | #define RNG_TYPE std::mt19937 42 | #endif 43 | 44 | using rng_t = RNG_TYPE; 45 | 46 | #if USE_STD 47 | 48 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 49 | std::uniform_int_distribution dist(0, range-1); 50 | 51 | return dist(rng); 52 | } 53 | 54 | #elif USE_BIASED_FP_MULT_LDEXP 55 | 56 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 57 | double zeroone = ldexp(rng(), -32); 58 | return range * zeroone; 59 | } 60 | 61 | #elif USE_BIASED_FP_MULT_SCALE 62 | 63 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 64 | double zeroone = 0x1.0p-32 * rng(); 65 | return range * zeroone; 66 | } 67 | 68 | #elif USE_BIASED_MOD 69 | 70 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 71 | return rng() % range; 72 | } 73 | 74 | #elif USE_DEBIASED_DIV 75 | 76 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 77 | uint32_t divisor = ((-range) / range) + 1; 78 | if (divisor == 0) 79 | return 0; 80 | for (;;) { 81 | uint32_t val = rng() / divisor; 82 | if (val < range) 83 | return val; 84 | } 85 | } 86 | 87 | #elif USE_DEBIASED_MODx2 88 | 89 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 90 | uint32_t t = (-range) % range; 91 | for (;;) { 92 | uint32_t r = rng(); 93 | if (r >= t) 94 | return r % range; 95 | } 96 | } 97 | 98 | #elif USE_DEBIASED_MODx2_MOPT 99 | 100 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 101 | uint32_t t = -range; 102 | if (t >= range) { 103 | t -= range; 104 | if (t >= range) 105 | t %= range; 106 | } 107 | for (;;) { 108 | uint32_t r = rng(); 109 | if (r >= t) 110 | return r % range; 111 | } 112 | } 113 | 114 | #elif USE_DEBIASED_MODx2_TOPT 115 | 116 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 117 | uint32_t r = rng(); 118 | if (r < range) { 119 | uint32_t t = (-range) % range; 120 | while (r < t) 121 | r = rng(); 122 | } 123 | return r % range; 124 | } 125 | 126 | #elif USE_DEBIASED_MODx2_TOPT_BOPT 127 | 128 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 129 | uint32_t r = rng(); 130 | if (range >= 1u<<31) { 131 | while(r >= range) 132 | r = rng(); 133 | return r; 134 | } 135 | if (r < range) { 136 | uint32_t t = (-range) % range; 137 | while (r < t) 138 | r = rng(); 139 | } 140 | return r % range; 141 | } 142 | 143 | #elif USE_DEBIASED_MODx2_TOPT_MOPT 144 | 145 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 146 | uint32_t r = rng(); 147 | if (r < range) { 148 | uint32_t t = -range; 149 | if (t >= range) { 150 | t -= range; 151 | if (t >= range) 152 | t %= range; 153 | } 154 | while (r < t) 155 | r = rng(); 156 | } 157 | return r % range; 158 | } 159 | 160 | #elif USE_DEBIASED_MODx2_TOPT_MOPTx2 161 | 162 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 163 | uint32_t r = rng(); 164 | if (r < range) { 165 | uint32_t t = -range; 166 | if (t >= range) { 167 | t -= range; 168 | if (t >= range) 169 | t %= range; 170 | } 171 | while (r < t) 172 | r = rng(); 173 | } 174 | if (r >= range) { 175 | r -= range; 176 | if (r >= range) 177 | r %= range; 178 | } 179 | return r; 180 | } 181 | 182 | #elif USE_DEBIASED_MODx1 183 | 184 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 185 | uint32_t x, r; 186 | do { 187 | x = rng(); 188 | r = x % range; 189 | } while (x - r > uint32_t(-range)); 190 | return r; 191 | } 192 | 193 | #elif USE_DEBIASED_MODx1_BOPT 194 | 195 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 196 | uint32_t x, r; 197 | if (range >= 1u<<31) { 198 | do { 199 | r = rng(); 200 | } while (r >= range); 201 | return r; 202 | } 203 | do { 204 | x = rng(); 205 | r = x % range; 206 | } while (x - r > uint32_t(-range)); 207 | return r; 208 | } 209 | 210 | #elif USE_DEBIASED_MODx1_MOPT 211 | 212 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 213 | uint32_t x, r; 214 | do { 215 | x = rng(); 216 | r = x; 217 | if (r >= range) { 218 | r -= range; 219 | if (r >= range) 220 | r %= range; 221 | } 222 | } while (x - r > uint32_t(-range)); 223 | return r; 224 | } 225 | 226 | #elif USE_BIASED_INT_MULT 227 | 228 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 229 | uint32_t x = rng(); 230 | uint64_t m = uint64_t(x) * uint64_t(range); 231 | return m >> 32; 232 | } 233 | 234 | #elif USE_DEBIASED_INT_MULT 235 | 236 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 237 | uint32_t t = (-range) % range; 238 | uint32_t l; 239 | uint64_t m; 240 | do { 241 | uint32_t x = rng(); 242 | m = uint64_t(x) * uint64_t(range); 243 | l = uint32_t(m); 244 | } while (l < t); 245 | return m >> 32; 246 | } 247 | 248 | 249 | #elif USE_DEBIASED_INT_MULT_TOPT 250 | 251 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 252 | uint32_t x = rng(); 253 | uint64_t m = uint64_t(x) * uint64_t(range); 254 | uint32_t l = uint32_t(m); 255 | if (l < range) { 256 | uint32_t t = (-range) % range; 257 | while (l < t) { 258 | x = rng(); 259 | m = uint64_t(x) * uint64_t(range); 260 | l = uint32_t(m); 261 | } 262 | } 263 | return m >> 32; 264 | } 265 | 266 | #elif USE_DEBIASED_INT_MULT_TOPT_BOPT 267 | 268 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 269 | uint32_t x = rng(); 270 | if (range >= 1ul<<31) { 271 | while(x >= range) 272 | x = rng(); 273 | return x; 274 | } 275 | uint64_t m = uint64_t(x) * uint64_t(range); 276 | uint32_t l = uint32_t(m); 277 | if (l < range) { 278 | uint32_t t = (-range) % range; 279 | while (l < t) { 280 | x = rng(); 281 | m = uint64_t(x) * uint64_t(range); 282 | l = uint32_t(m); 283 | } 284 | } 285 | return m >> 32; 286 | } 287 | 288 | #elif USE_DEBIASED_INT_MULT_TOPT_MOPT 289 | 290 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 291 | uint32_t x = rng(); 292 | uint64_t m = uint64_t(x) * uint64_t(range); 293 | uint32_t l = uint32_t(m); 294 | if (l < range) { 295 | uint32_t t = -range; 296 | if (t >= range) { 297 | t -= range; 298 | if (t >= range) 299 | t %= range; 300 | } 301 | while (l < t) { 302 | x = rng(); 303 | m = uint64_t(x) * uint64_t(range); 304 | l = uint32_t(m); 305 | } 306 | } 307 | return m >> 32; 308 | } 309 | 310 | #elif USE_DEBIASED_INT_MULT_TOPT_MOPT_BOPT 311 | 312 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 313 | uint32_t x = rng(); 314 | if (range >= 1u<<31) { 315 | while(x >= range) 316 | x = rng(); 317 | return x; 318 | } 319 | uint64_t m = uint64_t(x) * uint64_t(range); 320 | uint32_t l = uint32_t(m); 321 | if (l < range) { 322 | uint32_t t = -range; 323 | t -= range; 324 | if (t >= range) 325 | t %= range; 326 | while (l < t) { 327 | x = rng(); 328 | m = uint64_t(x) * uint64_t(range); 329 | l = uint32_t(m); 330 | } 331 | } 332 | return m >> 32; 333 | } 334 | 335 | #elif USE_BITMASK 336 | 337 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 338 | uint32_t mask = ~uint32_t(0); 339 | --range; 340 | mask >>= __builtin_clz(range|1); 341 | uint32_t x; 342 | do { 343 | x = rng() & mask; 344 | } while (x > range); 345 | return x; 346 | } 347 | 348 | #elif USE_BITMASK_ALT 349 | 350 | static uint32_t bounded_rand(rng_t& rng, uint32_t range) { 351 | --range; 352 | unsigned int zeros = __builtin_clz(range|1); 353 | uint32_t mask = (~uint32_t(0)) >> zeros; 354 | for (;;) { 355 | uint32_t r = rng(); 356 | uint32_t v = r & mask; 357 | if (v <= range) 358 | return v; 359 | unsigned int shift = 16; 360 | while (zeros >= shift) { 361 | r >>= shift; 362 | v = r & mask; 363 | if (v <= range) 364 | return v; 365 | shift = 32 - (32 - shift)/2; 366 | } 367 | } 368 | } 369 | 370 | #endif 371 | 372 | int main(int argc, char* argv[]) 373 | { 374 | uint64_t sum = 0; 375 | uint64_t seed; 376 | if (argc <= 1) { 377 | std::random_device rdev; 378 | seed = rdev(); 379 | seed <<= 32; 380 | seed |= rdev(); 381 | } else { 382 | seed = strtoul(argv[1], nullptr, 0); 383 | } 384 | rng_t rng(seed); 385 | #if RNG_HAS_DISTANCE 386 | rng_t rng_copy = rng; 387 | #endif 388 | Timer timer; 389 | 390 | // Large shuffle 391 | timer.start("Test 1"); 392 | for (uint32_t i = 0xffffffff; i > 0; --i) { 393 | uint32_t bval = bounded_rand(rng, i); 394 | assert(bval < i); 395 | sum += bval; 396 | } 397 | timer.done(); 398 | std::cout << "Sum1 = " << sum << "\n"; 399 | 400 | // Small shuffle 401 | sum = 0; 402 | timer.start("Test 2"); 403 | for (uint32_t j = 0; j < 0xffff; ++j) { 404 | for (uint32_t i = 0x0000ffff; i > 0; --i) { 405 | uint32_t bval = bounded_rand(rng, i); 406 | assert(bval < i); 407 | sum += bval; 408 | } 409 | } 410 | timer.done(); 411 | std::cout << "Sum2 = " << sum << "\n"; 412 | 413 | // All-ranges shuffle 414 | sum = 0; 415 | timer.start("Test 3"); 416 | for (uint32_t bit = 1; bit != 0; bit <<= 1) { 417 | for (uint32_t i = 0; i < 0x1000000; ++i) { 418 | uint32_t bound = bit | (i & (bit - 1)); 419 | uint32_t bval = bounded_rand(rng, bound); 420 | assert(bval < bound); 421 | sum += bval; 422 | } 423 | } 424 | timer.done(); 425 | std::cout << "Sum3 = " << sum << "\n"; 426 | 427 | // Small constant 428 | sum = 0; 429 | timer.start("Test 4"); 430 | for (uint32_t i = 0; i < 0x80000000; ++i) { 431 | uint32_t bval = bounded_rand(rng, 52); 432 | assert(bval < 52); 433 | sum += bval; 434 | } 435 | timer.done(); 436 | std::cout << "Sum4 = " << sum << "\n"; 437 | 438 | // Large constant 439 | sum = 0; 440 | timer.start("Test 5"); 441 | for (uint32_t i = 0; i < 0x80000000; ++i) { 442 | uint32_t bval = bounded_rand(rng, uint32_t(-52)); 443 | assert(bval < uint32_t(-52)); 444 | sum += bval; 445 | } 446 | timer.done(); 447 | std::cout << "Sum5 = " << sum << "\n"; 448 | 449 | #if RNG_HAS_DISTANCE 450 | std::cout << rng - rng_copy << " numbers used" << "\n"; 451 | #endif 452 | } 453 | 454 | -------------------------------------------------------------------------------- /bounded64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * A C++ implementation methods and benchmarks for random numbers in a range 3 | * (64-bit version) 4 | * 5 | * The MIT License (MIT) 6 | * 7 | * Copyright (c) 2018 Melissa E. O'Neill 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a 10 | * copy of this software and associated documentation files (the "Software"), 11 | * to deal in the Software without restriction, including without limitation 12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 | * and/or sell copies of the Software, and to permit persons to whom the 14 | * Software is furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | * DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "pcg_random.hpp" 34 | #include "timer.hpp" 35 | 36 | #ifdef RNG_INCLUDE 37 | #include RNG_INCLUDE 38 | #endif 39 | 40 | #ifndef RNG_TYPE 41 | #define RNG_TYPE std::mt19937 42 | #endif 43 | 44 | using rng_t = RNG_TYPE; 45 | 46 | #if USE_STD 47 | 48 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 49 | std::uniform_int_distribution dist(0, range-1); 50 | 51 | return dist(rng); 52 | } 53 | 54 | #elif USE_BIASED_FP_MULT_LDEXP 55 | 56 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 57 | long double zeroone = ldexpl(rng(), -64); 58 | return range * zeroone; 59 | } 60 | 61 | #elif USE_BIASED_FP_MULT_SCALE 62 | 63 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 64 | long double zeroone = 0x1.0p-64l * rng(); 65 | return range * zeroone; 66 | } 67 | 68 | #elif USE_BIASED_MOD 69 | 70 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 71 | return rng() % range; 72 | } 73 | 74 | #elif USE_DEBIASED_DIV 75 | 76 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 77 | uint64_t divisor = ((-range) / range) + 1; 78 | if (divisor == 0) 79 | return 0; 80 | for (;;) { 81 | uint64_t val = rng() / divisor; 82 | if (val < range) 83 | return val; 84 | } 85 | } 86 | 87 | #elif USE_DEBIASED_MODx2 88 | 89 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 90 | uint64_t t = (-range) % range; 91 | for (;;) { 92 | uint64_t r = rng(); 93 | if (r >= t) 94 | return r % range; 95 | } 96 | } 97 | 98 | #elif USE_DEBIASED_MODx2_MOPT 99 | 100 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 101 | uint64_t t = -range; 102 | if (t >= range) { 103 | t -= range; 104 | if (t >= range) 105 | t %= range; 106 | } 107 | for (;;) { 108 | uint64_t r = rng(); 109 | if (r >= t) 110 | return r % range; 111 | } 112 | } 113 | 114 | #elif USE_DEBIASED_MODx2_TOPT 115 | 116 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 117 | uint64_t r = rng(); 118 | if (r < range) { 119 | uint64_t t = (-range) % range; 120 | while (r < t) 121 | r = rng(); 122 | } 123 | return r % range; 124 | } 125 | 126 | #elif USE_DEBIASED_MODx2_TOPT_BOPT 127 | 128 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 129 | uint64_t r = rng(); 130 | if (range >= 1ul<<63) { 131 | while(r >= range) 132 | r = rng(); 133 | return r; 134 | } 135 | if (r < range) { 136 | uint64_t t = (-range) % range; 137 | while (r < t) 138 | r = rng(); 139 | } 140 | return r % range; 141 | } 142 | 143 | #elif USE_DEBIASED_MODx2_TOPT_MOPT 144 | 145 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 146 | uint64_t r = rng(); 147 | if (r < range) { 148 | uint64_t t = -range; 149 | if (t >= range) { 150 | t -= range; 151 | if (t >= range) 152 | t %= range; 153 | } 154 | while (r < t) 155 | r = rng(); 156 | } 157 | return r % range; 158 | } 159 | 160 | 161 | #elif USE_DEBIASED_MODx2_TOPT_MOPTx2 162 | 163 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 164 | uint64_t r = rng(); 165 | if (r < range) { 166 | uint64_t t = -range; 167 | if (t >= range) { 168 | t -= range; 169 | if (t >= range) 170 | t %= range; 171 | } 172 | while (r < t) 173 | r = rng(); 174 | } 175 | if (r >= range) { 176 | r -= range; 177 | if (r >= range) 178 | r %= range; 179 | } 180 | return r; 181 | } 182 | 183 | #elif USE_DEBIASED_MODx1 184 | 185 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 186 | uint64_t x, r; 187 | do { 188 | x = rng(); 189 | r = x % range; 190 | } while (x - r > uint64_t(-range)); 191 | return r; 192 | } 193 | 194 | #elif USE_DEBIASED_MODx1_BOPT 195 | 196 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 197 | uint64_t x, r; 198 | if (range >= 1ul<<63) { 199 | do { 200 | r = rng(); 201 | } while (r >= range); 202 | return r; 203 | } 204 | do { 205 | x = rng(); 206 | r = x % range; 207 | } while (x - r > uint64_t(-range)); 208 | return r; 209 | } 210 | 211 | #elif USE_DEBIASED_MODx1_MOPT 212 | 213 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 214 | uint64_t x, r; 215 | do { 216 | x = rng(); 217 | r = x; 218 | if (r >= range) { 219 | r -= range; 220 | if (r >= range) 221 | r %= range; 222 | } 223 | } while (x - r > uint64_t(-range)); 224 | return r; 225 | } 226 | 227 | #elif USE_BIASED_INT_MULT 228 | 229 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 230 | uint64_t x = rng(); 231 | __uint128_t m = __uint128_t(x) * __uint128_t(range); 232 | return m >> 64; 233 | } 234 | 235 | 236 | 237 | #elif USE_DEBIASED_INT_MULT 238 | 239 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 240 | uint64_t t = (-range) % range; 241 | uint64_t l; 242 | __uint128_t m; 243 | do { 244 | uint64_t x = rng(); 245 | m = __uint128_t(x) * __uint128_t(range); 246 | l = uint64_t(m); 247 | } while (l < t); 248 | return m >> 64; 249 | } 250 | 251 | 252 | #elif USE_DEBIASED_INT_MULT_TOPT 253 | 254 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 255 | uint64_t x = rng(); 256 | __uint128_t m = __uint128_t(x) * __uint128_t(range); 257 | uint64_t l = uint64_t(m); 258 | if (l < range) { 259 | uint64_t t = (-range) % range; 260 | while (l < t) { 261 | x = rng(); 262 | m = __uint128_t(x) * __uint128_t(range); 263 | l = uint64_t(m); 264 | } 265 | } 266 | return m >> 64; 267 | } 268 | 269 | #elif USE_DEBIASED_INT_MULT_TOPT_BOPT 270 | 271 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 272 | uint64_t x = rng(); 273 | if (range >= 1ul<<63) { 274 | while(x >= range) 275 | x = rng(); 276 | return x; 277 | } 278 | __uint128_t m = __uint128_t(x) * __uint128_t(range); 279 | uint64_t l = uint64_t(m); 280 | if (l < range) { 281 | uint64_t t = (-range) % range; 282 | while (l < t) { 283 | x = rng(); 284 | m = __uint128_t(x) * __uint128_t(range); 285 | l = uint64_t(m); 286 | } 287 | } 288 | return m >> 64; 289 | } 290 | 291 | #elif USE_DEBIASED_INT_MULT_TOPT_MOPT 292 | 293 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 294 | uint64_t x = rng(); 295 | __uint128_t m = __uint128_t(x) * __uint128_t(range); 296 | uint64_t l = uint64_t(m); 297 | if (l < range) { 298 | uint64_t t = -range; 299 | if (t >= range) { 300 | t -= range; 301 | if (t >= range) 302 | t %= range; 303 | } 304 | while (l < t) { 305 | x = rng(); 306 | m = __uint128_t(x) * __uint128_t(range); 307 | l = uint64_t(m); 308 | } 309 | } 310 | return m >> 64; 311 | } 312 | 313 | #elif USE_DEBIASED_INT_MULT_TOPT_MOPT_BOPT 314 | 315 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 316 | uint64_t x = rng(); 317 | if (range >= 1ul<<63) { 318 | while(x >= range) 319 | x = rng(); 320 | return x; 321 | } 322 | __uint128_t m = __uint128_t(x) * __uint128_t(range); 323 | uint64_t l = uint64_t(m); 324 | if (l < range) { 325 | uint64_t t = -range; 326 | t -= range; 327 | if (t >= range) 328 | t %= range; 329 | while (l < t) { 330 | x = rng(); 331 | m = __uint128_t(x) * __uint128_t(range); 332 | l = uint64_t(m); 333 | } 334 | } 335 | return m >> 64; 336 | } 337 | 338 | #elif USE_BITMASK 339 | 340 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 341 | uint64_t mask = ~uint64_t(0); 342 | --range; 343 | mask >>= __builtin_clzl(range|1); 344 | uint64_t x; 345 | do { 346 | x = rng() & mask; 347 | } while (x > range); 348 | return x; 349 | } 350 | 351 | #elif USE_BITMASK_ALT 352 | 353 | static uint64_t bounded_rand(rng_t& rng, uint64_t range) { 354 | --range; 355 | unsigned int zeros = __builtin_clzl(range|1); 356 | uint64_t mask = (~uint64_t(0)) >> zeros; 357 | for (;;) { 358 | uint64_t r = rng(); 359 | uint64_t v = r & mask; 360 | if (v <= range) 361 | return v; 362 | unsigned int shift = 32; 363 | while (zeros >= shift) { 364 | r >>= shift; 365 | v = r & mask; 366 | if (v <= range) 367 | return v; 368 | shift = 64 - (64 - shift)/2; 369 | } 370 | } 371 | } 372 | 373 | #endif 374 | 375 | int main(int argc, char* argv[]) 376 | { 377 | pcg_extras::pcg128_t sum = 0; 378 | using pcg_extras::operator<<; 379 | 380 | uint64_t seed; 381 | if (argc <= 1) { 382 | std::random_device rdev; 383 | seed = rdev(); 384 | seed <<= 32; 385 | seed |= rdev(); 386 | } else { 387 | seed = strtoul(argv[1], nullptr, 0); 388 | } 389 | rng_t rng(seed); 390 | #if RNG_HAS_DISTANCE 391 | rng_t rng_copy = rng; 392 | #endif 393 | Timer timer; 394 | 395 | // Large shuffle 396 | timer.start("Test 1"); 397 | for (uint32_t i = 0xffffffff; i > 0; --i) { 398 | uint64_t bound = (uint64_t(i)<<32) | i; 399 | uint64_t bval = bounded_rand(rng, bound ); 400 | assert(bval < bound); 401 | sum += bval; 402 | } 403 | timer.done(); 404 | std::cout << "Sum1 = " << sum << "\n"; 405 | 406 | // Small shuffle 407 | sum = 0; 408 | timer.start("Test 2"); 409 | for (uint64_t i = 0xffffffff; i > 0; --i) { 410 | uint64_t bval = bounded_rand(rng, i); 411 | assert(bval < i); 412 | sum += bval; 413 | } 414 | timer.done(); 415 | std::cout << "Sum2 = " << sum << "\n"; 416 | 417 | // All-ranges shuffle 418 | sum = 0; 419 | timer.start("Test 3"); 420 | for (uint64_t bit = 1; bit != 0; bit <<= 1) { 421 | for (uint32_t i = 0; i < 0x800000; ++i) { 422 | uint64_t bound = bit | (i & (bit - 1)); 423 | uint64_t bval = bounded_rand(rng, bound); 424 | assert(bval < bound); 425 | sum += bval; 426 | } 427 | } 428 | timer.done(); 429 | std::cout << "Sum3 = " << sum << "\n"; 430 | 431 | // Small constant 432 | sum = 0; 433 | timer.start("Test 4"); 434 | for (uint32_t i = 0; i < 0x80000000; ++i) { 435 | uint64_t bval = bounded_rand(rng, 52); 436 | assert(bval < 52); 437 | sum += bval; 438 | } 439 | timer.done(); 440 | std::cout << "Sum4 = " << sum << "\n"; 441 | 442 | // Large constant 443 | sum = 0; 444 | timer.start("Test 5"); 445 | for (uint32_t i = 0; i < 0x80000000; ++i) { 446 | uint64_t bval = bounded_rand(rng, uint64_t(-52)); 447 | assert(bval < uint64_t(-52)); 448 | sum += bval; 449 | } 450 | timer.done(); 451 | std::cout << "Sum4 = " << sum << "\n"; 452 | 453 | #if RNG_HAS_DISTANCE 454 | std::cout << rng - rng_copy << " numbers used" << "\n"; 455 | #endif 456 | } 457 | -------------------------------------------------------------------------------- /download-gists.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DOWNLOADER="curl -OL" 4 | UNZIP=unzip 5 | RM="rm -f" 6 | ECHO=echo 7 | TOUCH=touch 8 | 9 | if [ -e .have_gists ] 10 | then 11 | $ECHO "PRNGs and adapters are downloaded. Use ./remove-gists.sh first." 12 | exit 1 13 | fi 14 | 15 | $ECHO 'Downloading PRNGs and adapters...' 16 | 17 | $DOWNLOADER 'https://gist.github.com/imneme/f0fe8877e4deb3f6b9200a17c18bf155/raw//chacha.hpp' 18 | $DOWNLOADER 'https://gist.github.com/imneme/4f2bf4b4f3a221ef051cf108d6b64d5a/raw/arc4.hpp' 19 | $DOWNLOADER 'https://gist.github.com/imneme/6179748664e88ef3c34860f44309fc71/raw/splitmix.hpp' 20 | $DOWNLOADER 'https://gist.github.com/imneme/3eb1bcc5418c4ae83c4c6a86d9cbb1cd/raw/xoshiro.hpp' 21 | $DOWNLOADER 'https://gist.github.com/imneme/aeae7628565f15fb3fef54be8533e39c/raw/lehmer.hpp' 22 | $DOWNLOADER 'https://gist.github.com/imneme/85cff47d4bad8de6bdeb671f9c76c814/raw/jsf.hpp' 23 | $DOWNLOADER 'https://gist.github.com/imneme/7a783e20f71259cc13e219829bcea4ac/raw/gjrand.hpp' 24 | $DOWNLOADER 'https://gist.github.com/imneme/f1f7821f07cf76504a97f6537c818083/raw/sfc.hpp' 25 | $DOWNLOADER 'https://gist.github.com/imneme/9b769cefccac1f2bd728596da3a856dd/raw/xorshift.hpp' 26 | $DOWNLOADER 'https://gist.github.com/imneme/f76f4bb7b7f67ff0850199ab7c077bf7/raw/xoroshiro.hpp' 27 | $DOWNLOADER 'https://github.com/imneme/pcg-cpp/archive/master.zip' 28 | $UNZIP master.zip 29 | $RM master.zip 30 | 31 | $TOUCH .have_gists 32 | -------------------------------------------------------------------------------- /gen-makefile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | GPLUSPLUS="g++-8 -Wall -O3 -march=native -std=c++17 -g" 4 | CLANGPLUSPLUS="clang++ -Wall -O3 -march=native -std=c++17 -g" 5 | CLANGLIBPATH=`which clang++` 6 | CLANGLIBPATH=$CLANGLIBPATH:h/../include/c++/v1 7 | GPLUSPLUS_USELIBCPP="-nostdinc++ -I$CLANGLIBPATH -nodefaultlibs -lc++ -lc++abi -lm -lc -lgcc_s -lgcc" # Linux 8 | GPLUSPLUS_USELIBCPP="-nostdinc++ -I$CLANGLIBPATH -nodefaultlibs -lc++ -lc++abi -lm -lSystem -lgcc" # OS X 9 | 10 | EXECDIR=tests 11 | 12 | { 13 | 14 | while read -A line 15 | do 16 | foreach method (`perl -ne 'print if s/^#.*if.*USE_//' bounded32.cpp`) 17 | echo $GPLUSPLUS bounded32.cpp -Ipcg-cpp-master/include -DUSE_$method -DRNG_INCLUDE=$line[1] -DRNG_TYPE=$line[2] $line[3,-1] -o $EXECDIR/bounded32.$line[2].$method.gcc 18 | echo $CLANGPLUSPLUS bounded32.cpp -Ipcg-cpp-master/include -DUSE_$method -DRNG_INCLUDE=$line[1] -DRNG_TYPE=$line[2] $line[3,-1] -o $EXECDIR/bounded32.$line[2].$method.clang 19 | end 20 | foreach method (STD) 21 | echo $GPLUSPLUS bounded32.cpp -Ipcg-cpp-master/include -DUSE_$method -DRNG_INCLUDE=$line[1] -DRNG_TYPE=$line[2] $line[3,-1] $GPLUSPLUS_USELIBCPP -o $EXECDIR/bounded32.$line[2].$method-libc++.gcc 22 | echo $CLANGPLUSPLUS -stdlib=libc++ bounded32.cpp -Ipcg-cpp-master/include -DUSE_$method -DRNG_INCLUDE=$line[1] -DRNG_TYPE=$line[2] $line[3,-1] -o $EXECDIR/bounded32.$line[2].$method-libc++.clang 23 | end 24 | done < schemes-32.dat 25 | 26 | while read -A line 27 | do 28 | foreach method (`perl -ne 'print if s/^#.*if.*USE_//' bounded64.cpp`) 29 | echo $GPLUSPLUS bounded64.cpp -Ipcg-cpp-master/include -DUSE_$method -DRNG_INCLUDE=$line[1] -DRNG_TYPE=$line[2] $line[3,-1] -o $EXECDIR/bounded64.$line[2].$method.gcc 30 | echo $CLANGPLUSPLUS bounded64.cpp -Ipcg-cpp-master/include -DUSE_$method -DRNG_INCLUDE=$line[1] -DRNG_TYPE=$line[2] $line[3,-1] -o $EXECDIR/bounded64.$line[2].$method.clang 31 | end 32 | foreach method (STD) 33 | echo $GPLUSPLUS bounded64.cpp -Ipcg-cpp-master/include -DUSE_$method -DRNG_INCLUDE=$line[1] -DRNG_TYPE=$line[2] $line[3,-1] $GPLUSPLUS_USELIBCPP -o $EXECDIR/bounded64.$line[2].$method-libc++.gcc 34 | echo $CLANGPLUSPLUS -stdlib=libc++ bounded64.cpp -Ipcg-cpp-master/include -DUSE_$method -DRNG_INCLUDE=$line[1] -DRNG_TYPE=$line[2] $line[3,-1] -o $EXECDIR/bounded64.$line[2].$method-libc++.clang 35 | end 36 | done < schemes-64.dat 37 | 38 | } | perl -lane 'BEGIN { print "# This Makefile was auto-generated by gen-makefile.sh\n\nall: targets\n" } s/(["<])(.*?)([>"])/\\$1$2\\$3/g; m/(\w+\.cpp)/ or die "?"; print "$F[-1]: $1\n\t$_\n"; push @execs, $F[-1]; END { print "clean:\n\trm -f @execs\n"; print "targets: @execs\n"; }' > Makefile 39 | 40 | mkdir -p $EXECDIR 41 | -------------------------------------------------------------------------------- /gen-summary-tsv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./summarize.pl --method out/bounded32.*.out > bounded32-methods.tsv 4 | ./summarize.pl --method out/bounded64.*.out > bounded64-methods.tsv 5 | 6 | ./summarize.pl --method out/bounded32.mt19937*.*.out > bounded32-methods-mt.tsv 7 | ./summarize.pl --method out/bounded64.mt19937*.*.out > bounded64-methods-mt.tsv 8 | 9 | ./summarize.pl --prng out/bounded32.*.out > bounded32-prngs.tsv 10 | ./summarize.pl --prng out/bounded64.*.out > bounded64-prngs.tsv 11 | -------------------------------------------------------------------------------- /gen-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # Some random seeds: 4 | # 0x2ac4a88cb54956ad 0x337fee5ab97681b0 0xc7a93e3d4659a04f 0x63ba3f7a8a871e80 0x9346051b755d9994 0x4cb3a4475c6a21df 0xe5c4de8f09ec5eda 0x4e3554d545284126 0x6d93eddf288540a9 0x7f162b6419d84325 0x989ae787218f6f5e 0xa6c48681e292efa3 0xeac54f152aabff6d 0x7e3cb652120a0a55 0xc02f720892b0c4d4 5 | 6 | for seed in "$@" 7 | do 8 | # Avoid picking up any .dSym files by looing for trailing 'g' (clang) 9 | # or 'c' (gcc) 10 | for prog in tests/*[gc] 11 | do 12 | echo "$prog $seed > out/$prog:t.$seed.out" 13 | done 14 | done | \ 15 | perl -e 'use strict; my @lines = (<>); while (@lines) { print splice @lines, rand(@lines), 1; }' | \ 16 | perl -lane 'BEGIN { print "# This Makefile was auto-generated by gen-tests.sh\n\nall: targets\n" } m{(tests/\S+)} or die "?"; print "$F[-1]: $1\n\t$_\n"; push @outs, $F[-1]; END { print "clean:\n\trm -f @outs\n"; print "targets: @outs\n"; }' > Makefile.test 17 | 18 | mkdir -p out 19 | -------------------------------------------------------------------------------- /remove-gists.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ECHO=echo 4 | RM="rm -f" 5 | 6 | if [ ! -e .have_gists ] 7 | then 8 | $ECHO "PRNGs and adapters aren't downloaded." 9 | exit 1 10 | fi 11 | 12 | $ECHO 'Removing PRNGs and adapters...' 13 | 14 | $RM arc4.hpp chacha.hpp gjrand.hpp jsf.hpp lehmer.hpp sfc.hpp splitmix.hpp xoroshiro.hpp xorshift.hpp xoshiro.hpp 15 | $RM -r pcg-cpp-master 16 | 17 | $RM .have_gists 18 | -------------------------------------------------------------------------------- /schemes-32.dat: -------------------------------------------------------------------------------- 1 | "gjrand.hpp" gjrand32 2 | "jsf.hpp" jsf32 3 | mt19937 -URNG_TYPE -DRNG_TYPE=std::mt19937 4 | "pcg_random.hpp" pcg32_fast 5 | "pcg_random.hpp" pcg32 6 | "sfc.hpp" sfc32 7 | "splitmix.hpp" splitmix32 8 | "xoroshiro.hpp" xoroshiro64plus32 9 | "xorshift.hpp" xorshift64star32a 10 | "xoshiro.hpp" xoshiro128plus32 11 | "xoshiro.hpp" xoshiro128starstar32 12 | "arc4.hpp" arc4_rand32 13 | "chacha.hpp" chacha8r 14 | -------------------------------------------------------------------------------- /schemes-64.dat: -------------------------------------------------------------------------------- 1 | "gjrand.hpp" gjrand64 2 | "jsf.hpp" jsf64 3 | "lehmer.hpp" mcg128_fast 4 | "lehmer.hpp" mcg128 5 | mt19937_64 -URNG_TYPE -DRNG_TYPE=std::mt19937_64 6 | "pcg_random.hpp" pcg64_fast 7 | "pcg_random.hpp" pcg64 8 | "sfc.hpp" sfc64 9 | "splitmix.hpp" splitmix64 10 | "xoroshiro.hpp" xoroshiro128plus64 11 | "xorshift.hpp" xorshift128star64a 12 | "xoshiro.hpp" xoshiro256plus64 13 | "xoshiro.hpp" xoshiro256starstar64 14 | -------------------------------------------------------------------------------- /summarize.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -lw 2 | 3 | use strict; 4 | use Getopt::Long; 5 | 6 | my %timeFor; 7 | my $byPRNG; 8 | my $byMethod; 9 | my $byCompiler; 10 | my $bySeed; 11 | 12 | GetOptions ("method" => \$byMethod, 13 | "prng" => \$byPRNG, 14 | "compiler" => \$byCompiler, 15 | "seed" => \$bySeed) 16 | or die("Error in command line arguments\n"); 17 | 18 | 19 | foreach my $file (@ARGV) { 20 | my $id = $file; 21 | $id =~ s{\.([^.]+)\.([^.]+)\.([^.]+)\.([^.]+)\.}{ 22 | ($byPRNG ? ".$1" : "") . ($byMethod ? ".$2" : "") . 23 | ($byCompiler ? ".$3" : "") . ($bySeed ? ".$4" : "") . "."}e; 24 | $id =~ s{.*/}{}; 25 | $id =~ s{\.out}{}; 26 | open my $fh, "<", $file or die "Can't open '$file' ($!)"; 27 | while (<$fh>) { 28 | chomp; 29 | next unless m{^(.*?) completed \((\S+) seconds\)}; 30 | push @{$timeFor{$id}{$1}}, $2; 31 | } 32 | } 33 | 34 | foreach my $id (sort keys %timeFor) { 35 | my @cols; 36 | foreach my $kind (sort keys %{$timeFor{$id}}) { 37 | my $sum = 0; 38 | $sum += log $_ foreach @{$timeFor{$id}{$kind}}; 39 | my $avg = $sum/@{$timeFor{$id}{$kind}}; 40 | push @cols, exp $avg; 41 | } 42 | print join("\t", $id, @cols); 43 | } 44 | -------------------------------------------------------------------------------- /timer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_HPP_INCLUDED 2 | #define TIMER_HPP_INCLUDED 3 | 4 | /* 5 | * A C++ implementation of a simple timer helper class. 6 | * 7 | * The MIT License (MIT) 8 | * 9 | * Copyright (c) 2018 Melissa E. O'Neill 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a 12 | * copy of this software and associated documentation files (the "Software"), 13 | * to deal in the Software without restriction, including without limitation 14 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 15 | * and/or sell copies of the Software, and to permit persons to whom the 16 | * Software is furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in 19 | * all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | * DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | struct Timer 34 | { 35 | std::chrono::time_point start_; 36 | const char* what_ = nullptr; 37 | 38 | Timer() = default; 39 | 40 | Timer(const char* what) 41 | { 42 | start(what); 43 | } 44 | 45 | void start(const char* what) { 46 | what_ = what; 47 | std::cout << what_ << " started...\n"; 48 | start_ = std::chrono::system_clock::now(); 49 | } 50 | 51 | void done() { 52 | auto end = std::chrono::system_clock::now(); 53 | std::chrono::duration elapsed_seconds = end-start_; 54 | std::cout << what_ << " completed (" << elapsed_seconds.count() 55 | << " seconds)\n"; 56 | } 57 | }; 58 | 59 | #endif // TIMER_HPP_INCLUDED 60 | --------------------------------------------------------------------------------