5

The method that I am considering is from an answer to Generating random integer from a range.

#include <random>

std::random_device rd;     // only used once to initialise (seed) engine
std::mt19937 rng(rd());    // random-number engine used (Mersenne-Twister in this case)
std::uniform_int_distribution<int> uni(min,max); // guaranteed unbiased

auto random_integer = uni(rng);

I'm also willing to use the rand() approach with srand(time(NULL)).

How expensive are these approaches? Is one much faster than the other?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Josh
  • 313
  • 3
  • 9
  • 22
    Build a benchmark test and find out. If you care about performance, you need to measure it. – Igor Tandetnik Jul 17 '16 at 14:51
  • 1
    If you want speed with pretty good quality (much better than traditional `rand()`), consider xorshift* or xorshift+. The latter vectorizes nicely, letting you generate 32 bytes of randomness (with AVX2) about as fast as you could generate 8 bytes with scalar. https://prng.di.unimi.it/ . (A few versions of the constants are floating around; I recommend the upstream site, not wikipedia.) – Peter Cordes Jul 16 '22 at 02:11
  • This is used as an example in [a meta question](https://meta.stackoverflow.com/questions/419271/why-are-questions-about-performance-discouraged/419276#419276). – Peter Mortensen Jul 22 '22 at 13:21

2 Answers2

5

Performance depends greatly on the generator you use (which in turn depends greatly on the quality of the numbers you need).

For example, std::mt19937 is much faster than std::random_device, but it produces pseudo random numbers. This is fine for most purposes, if you don't need cryptographically safe random numbers. But even if you do, random_device can produce raw entropy at a rate of about 50 MB/sec on my machine—how much randomness do you really need? (mt19937 generates about orders of magnitudes more than that if needed).

Avoid rand(). It just has very poor properties and a very low period.

See also Rand Considered Harmful.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
  • Better link: https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful – Baum mit Augen Jul 17 '16 at 15:03
  • It might be useful to note that `std::random_device` performance depends on a lot of factors, including which compiler you use (gcc and clang have different implementations), and even how busy the machine you're running your program on is (because of entropy). It is allowed to take as much time as it needs, so you can't necessarily assume your rate will even be in the same ballpark as 50MB/sec at any given time. – Wander Nauta Jul 17 '16 at 15:07
  • 1
    True, but I was just trying to illustrate a point. Pseudo-random == fast and usually good enough. Real random == slower, but usually fast enough. Conclusion, unless you are doing something silly, both are usually more than fast enough for their intended purpose. – Jesper Juhl Jul 17 '16 at 15:12
3

I could write that the performance depends on both implementation and hardware, but it would be as correct as useless. One example of performance would be more useful.

Dell Latitude E7240 laptop (2013), Linux, g++ 4.8.4, and the -O3 flag:

#include <cstdlib>
#include <iostream>

int main(int argc, const char** argv) {
    const bool bPlain = (argv[1][0] == '-');
    if (bPlain)
        argv++;
    int n = atoi(argv[1]);
    int sum = 0;
    if (bPlain)
        for (int i=0; i<n; i++)
            sum |= i;
    else
        for (int i=0; i<n; i++)
            sum |= rand();
    // To prevent the compiler from optimizing away the loop
    if (sum == 0)
        std::cout << sum << std::endl;
}
[~/CPP] time ./randbm 1000000000
9.049u 0.000s 0:09.05 99.8%    0+0k 0+0io 0pf+0w
[~/CPP] time ./randbm 1000000000
9.059u 0.000s 0:09.06 99.8%    0+0k 0+0io 0pf+0w
[~/CPP] time ./randbm 1000000000
9.040u 0.008s 0:09.05 99.8%    0+0k 0+0io 0pf+0w
[~/CPP] time ./randbm - 1000000000
0.192u 0.000s 0:00.20 95.0%    0+0k 0+0io 0pf+0w
[~/CPP] time ./randbm - 1000000000
0.172u 0.000s 0:00.18 94.4%    0+0k 0+0io 0pf+0w
[~/CPP] time ./randbm - 1000000000
0.185u 0.004s 0:00.20 90.0%    0+0k 0+0io 0pf+0w

So, in this particular case, one call to rand() takes roughly 9 nanoseconds, while one loop iteration takes roughly 0.2 nanoseconds.

Using random is slower. Adding #include <random> and replacing the relevant part of the code by:

std::random_device rd;     // only used once to initialise (seed) engine
std::mt19937 rng(rd());    // random-number engine used (Mersenne-Twister in this case)
std::uniform_int_distribution<int> uni(0, 1048575);

if (bPlain)
    for (int i=0; i<n; i++)
        sum |= i;
else
    for (int i=0; i<n; i++)
        sum |= uni(rng);

we get (notice we do 108 runs, not 109):

[~/CPP] time ./randbm2 100000000
2.478u 0.003s 0:02.49 99.1%    0+0k 0+0io 0pf+0w
[~/CPP] time ./randbm2 100000000
2.471u 0.004s 0:02.47 100.0%    0+0k 0+0io 0pf+0w
[~/CPP] time ./randbm2 100000000
2.445u 0.007s 0:02.48 98.3%    0+0k 0+0io 0pf+0w
[~/CPP] time ./randbm2 100000000
2.497u 0.004s 0:02.50 99.6%    0+0k 0+0io 0pf+0w
[~/CPP] time ./randbm2 100000000
2.482u 0.011s 0:02.49 100.0%    0+0k 0+0io 0pf+0w

Producing a random number this way takes roughly 25 nanoseconds. However, uni, unlike rand(), also inserts the number into the interval.

Is that extra work important? No. If, for example, you do

sum |= (rand() % 1048576);

the time increases from 9 to 9.5 nanoseconds. If the number is not a power of 2, e. g.

sum |= (rand() % 1000000);

It takes 10 nanoseconds. Other reasonable methods of inserting the number into the interval take roughly the same time.

So, for one particular configuration, rand() itself takes roughly 9 nanoseconds; together with inserting the random number into the interval, it takes roughly 9.5-10 nanoseconds; std::mt19937 with uniform_int_distribution<int> takes roughly 25 nanoseconds.

I hope you are not one of those who confuse nanoseconds with microseconds!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user31264
  • 6,557
  • 3
  • 26
  • 40
  • 3
    Please don't advocate use of modulo arithmetic for generating uniforms in other than the default range. It introduces [modulo bias](http://stackoverflow.com/a/10984975/2166798). – pjs Jul 18 '16 at 00:14
  • I wrote that "Other reasonable methods of inserting the number into the interval take roughly the same time." I discuss speed, not quality of random numbers. Finally, for many implementations the quality of `rand()` is poor to start with. – user31264 Jul 18 '16 at 01:32