我整天都在为此苦苦挣扎,我试图在我的 CUDA 代码中为线程获取一个随机数生成器。我浏览了所有论坛,是的,这个话题出现了很多,但我花了几个小时试图解开各种代码,但无济于事。如果有人知道一个简单的方法,可能是一个可以调用的设备内核返回一个介于 0 和 1 之间的随机浮点数,或者一个我可以转换的整数,我将不胜感激。
同样,我希望在内核中使用随机数,就像 rand() 一样。
提前致谢
对于任何有兴趣的人,您现在可以通过cuRAND进行操作。
我不确定我理解你为什么需要一些特别的东西。任何传统的 PRNG 都应该或多或少地直接移植。线性同余应该可以正常工作。你有一些你想要建立的特殊属性吗?
根据您的应用程序,您应该谨慎使用 LCG,而不考虑流(每个线程一个流)是否会重叠。您可以使用 LCG 实现跨越式,但是您需要有足够长的周期 LCG 以确保序列不会重复。
一个跳跃式示例可能是:
template <typename ValueType>
__device__ void leapfrog(unsigned long &a, unsigned long &c, int leap)
{
unsigned long an = a;
for (int i = 1 ; i < leap ; i++)
an *= a;
c = c * ((an - 1) / (a - 1));
a = an;
}
template <typename ValueType>
__device__ ValueType quickrand(unsigned long &seed, const unsigned long a, const unsigned long c)
{
seed = seed * a;
return seed;
}
template <typename ValueType>
__global__ void mykernel(
unsigned long *d_seeds)
{
// RNG parameters
unsigned long a = 1664525L;
unsigned long c = 1013904223L;
unsigned long ainit = a;
unsigned long cinit = c;
unsigned long seed;
// Generate local seed
seed = d_seeds[bid];
leapfrog<ValueType>(ainit, cinit, tid);
quickrand<ValueType>(seed, ainit, cinit);
leapfrog<ValueType>(a, c, blockDim.x);
...
}
但是在大多数情况下,该发电机的周期可能是不够的。
老实说,我会考虑使用第三方库,例如NAG。SDK 中也有一些批处理生成器,但在这种情况下,这可能不是您想要的。
编辑
由于这刚刚获得投票,我认为值得更新的是cuRAND,正如该问题的最新答案所提到的那样,它是可用的,并提供了许多生成器和分布。这绝对是最容易开始的地方。
最好的方法是编写自己的设备函数,这是一个
void RNG()
{
unsigned int m_w = 150;
unsigned int m_z = 40;
for(int i=0; i < 100; i++)
{
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
cout <<(m_z << 16) + m_w << endl; /* 32-bit result */
}
}
它会给你 100 个随机数和 32 位结果。
如果你想要一些 1 到 1000 之间的随机数,你也result%1000
可以在消费点或生成点取 :
((m_z << 16) + m_w)%1000
更改 m_w 和 m_z 起始值(在示例中为 150 和 40)可以让您每次都获得不同的结果。您可以将threadIdx.x
其用作其中之一,这应该每次都会为您提供不同的伪随机序列。
我想补充一点,它的工作速度比 rand() 函数快 2 倍,而且效果很好;)
我认为对这个问题的任何讨论都需要回答 Zenna 的原始请求,那就是线程级 实现。特别是可以从内核或线程中调用的设备函数。对不起,如果我过度使用“粗体”短语,但我真的认为到目前为止的答案并没有完全解决这里所寻求的内容。
cuRAND 库是您最好的选择。我很欣赏人们想要重新发明轮子(它让人欣赏并更正确地使用第 3 方库),但高性能高质量数字生成器数量众多且经过良好测试。我可以推荐的最佳信息是这里不同生成器上 GSL 库的文档:http ://www.gnu.org/software/gsl/manual/html_node/Random-number-generator-algorithms.html
对于任何严肃的代码,最好使用数学家/计算机科学家一遍又一遍地寻找系统弱点的主要算法之一。“mersenne twister”是一个周期(重复循环)大约为 10^6000(MT19997 算法意味着“Mersenne Twister 2^19997”)的东西,它特别适合 Nvidia 在线程级别使用使用线程 ID 调用作为种子的相同经纱。请参阅此处的论文:http: //developer.download.nvidia.com/compute/cuda/2_2/sdk/website/projects/MersenneTwister/doc/MersenneTwister.pdf。我实际上正在努力使用这个库来实现一些东西,如果我让它正常工作,我会发布我的代码。Nvidia 在其文档站点上有一些当前 CUDA 工具包的示例。
注意:为了记录,我不为 Nvidia 工作,但我承认他们为 CUDA 的文档和抽象设计是迄今为止我印象深刻的东西。
我还没有为 CUDA 找到一个好的并行数生成器,但是我确实找到了一个基于学术研究的并行随机数生成器:http: //sprng.cs.fsu.edu/
你可以试试用于 GPU 的 Mersenne Twister
它基于面向 SIMD 的 Fast Mersenne Twister (SFMT),这是一种非常快速且可靠的随机数生成器。它通过了随机数生成器的 Marsaglias DIEHARD 测试。
如果您在 Numba for Python 中使用 cuda.jit,这个随机数生成器很有用。