使用C ++ 11生成高效的随机数<random>

时间:2016-03-10 11:22:03

标签: c++ performance c++11 random

我试图理解如何使用C ++ 11随机数生成功能。我担心的是表现。

假设我们需要在0..k之间生成一系列随机整数,但每一步都会k更改。最好的方法是什么?

示例:

for (int i=0; i < n; ++i) {
    int k = i; // of course this is more complicated in practice
    std::uniform_int_distribution<> dist(0, k);
    int random_number = dist(engine);
    // do something with random number
}

<random>标头提供的分发非常方便。但它们对用户来说是不透明的,因此我无法轻易预测它们的表现。例如,上面dist的构造会导致多少(如果有的话)运行时开销,这一点尚不清楚。

相反,我可以使用像

这样的东西
std::uniform_real_distribution<> dist(0.0, 1.0);
for (int i=0; i < n; ++i) {
    int k = i; // of course this is more complicated in practice
    int random_number = std::floor( (k+1)*dist(engine) );
    // do something with random number
}

避免在每次迭代中构造一个新对象。

随机数通常用于性能很重要的数值模拟中。在这些情况下使用<random>的最佳方法是什么?

请不要回答“简介”。分析是有效优化的一部分,但是对如何使用库以及该库的性能特征有很好的理解。如果答案是它依赖于标准库实现,或者唯一知道的方法是对其进行分析,那么我宁愿不使用<random>中的分布。相反,我可以使用我自己的实现,这对我来说是透明的,并且在必要时更容易优化。

2 个答案:

答案 0 :(得分:6)

您可以做的一件事是拥有一个永久的发布对象,这样您每次只能创建param_type对象:

template<typename Integral>
Integral randint(Integral min, Integral max)
{
    using param_type =
        typename std::uniform_int_distribution<Integral>::param_type;

    // only create these once (per thread)
    thread_local static std::mt19937 eng {std::random_device{}()};
    thread_local static std::uniform_int_distribution<Integral> dist;

    // presumably a param_type is cheaper than a uniform_int_distribution
    return dist(eng, param_type{min, max});
}

答案 1 :(得分:2)

为了最大限度地提高性能,首先要考虑不同的PRNG,例如 xorshift128 + 。据报道,对于64位随机数,其速度是mt19937的两倍多;见http://xorshift.di.unimi.it/。它可以用几行代码实现。

此外,如果您不需要“完美平衡”均匀分布,并且k远小于2^64(可能是),我建议简单地写一下:

uint64_t temp = engine_64(); // generates 0 <= temp < 2^64
int random_number = temp % (k + 1); // crop temp to 0,...,k

但请注意,整数除法/模运算并不便宜。例如,在Intel Haswell处理器上,64位数字需要39-103个处理器周期,这可能比调用MT19937或xorshift +引擎要长得多。