使用Random123的OpenCL随机数

使用Random123的OpenCL随机数,random,opencl,Random,Opencl,我一直在看这个库和相关的引用: 一个神秘的人来到我的摊位,问我对用OpenCL生成随机数了解多少。我告诉他Mersenne Twister的实现,但他对此印象不深。他告诉我一篇新的技术论文,解释了如何通过组合整数计数器和分组密码在GPU上生成随机数。他恭敬地说,基于计数器的随机数生成器(CBRNG)产生的数字比MT具有更大的统计随机性,并且速度更快 我能够使用以下内核运行演示: __kernel void counthits(unsigned n, __global uint2 *hitsp)

我一直在看这个库和相关的引用:

一个神秘的人来到我的摊位,问我对用OpenCL生成随机数了解多少。我告诉他Mersenne Twister的实现,但他对此印象不深。他告诉我一篇新的技术论文,解释了如何通过组合整数计数器和分组密码在GPU上生成随机数。他恭敬地说,基于计数器的随机数生成器(CBRNG)产生的数字比MT具有更大的统计随机性,并且速度更快

我能够使用以下内核运行演示:

__kernel void counthits(unsigned n, __global uint2 *hitsp) {
    unsigned tid = get_global_id(0);
    unsigned hits = 0, tries = 0;
    threefry4x32_key_t k = {{tid, 0xdecafbad, 0xfacebead, 0x12345678}};
    threefry4x32_ctr_t c = {{0, 0xf00dcafe, 0xdeadbeef, 0xbeeff00d}};
    while (tries < n) {
        union {
            threefry4x32_ctr_t c;
            int4 i;
        } u;
        c.v[0]++;
        u.c = threefry4x32(c, k);
        long x1 = u.i.x, y1 = u.i.y;
        long x2 = u.i.z, y2 = u.i.w;
        if ((x1*x1 + y1*y1) < (1L<<62)) {
            hits++;
        }
        tries++;
        if ((x2*x2 + y2*y2) < (1L<<62)) {
            hits++;
        }
        tries++;
    }
    hitsp[tid].x = hits;
    hitsp[tid].y = tries;
}
\uuuu内核无效计数命中数(无符号n,\uuuu全局uint2*hitsp){
无符号tid=获取全局id(0);
未签名的点击次数=0,尝试次数=0;
三个fry4x32_key_t k={{tid,0xdecafbad,0xfacebead,0x12345678};
三个fry4x32_ctr_t c={{0,0xf00dcafe,0xdeadbeef,0xbeeff00d};
while(尝试如果((x1*x1+y1*y1)<(1L我无法帮助您处理库本身,但我可以告诉您,在OpenCL中生成随机数的最常见方法是在调用内核之间保存一些状态

随机数生成器通常使用一个状态,从该状态生成一个新状态和一个随机数。实际上,这一点都不复杂:只需传递一个保存状态的额外数组。在我的代码中,我实现了如下随机数:

uint rand_uint(uint2* rvec) {  //Adapted from http://cas.ee.ic.ac.uk/people/dt10/research/rngs-gpu-mwc64x.html
    #define A 4294883355U
    uint x=rvec->x, c=rvec->y; //Unpack the state
    uint res = x ^ c;          //Calculate the result
    uint hi = mul_hi(x,A);     //Step the RNG
    x = x*A + c;
    c = hi + (x<c);
    *rvec = (uint2)(x,c);      //Pack the state back up
    return res;                //Return the next result
    #undef A
}
inline float rand_float(uint2* rvec) {
    return (float)(rand_uint(rvec)) / (float)(0xFFFFFFFF);
}
__kernel void my_kernel(/*more arguments*/ __global uint2* randoms) {
    int index = get_global_id(0);
    uint2 rvec = randoms[index];

    //Call rand_uint or rand_float a number of times with "rvec" as argument.
    //These calls update "rvec" with new state, and return a random number

    randoms[index] = rvec;
}
uint rand_uint(uint2*rvec){//改编自http://cas.ee.ic.ac.uk/people/dt10/research/rngs-gpu-mwc64x.html
#定义一个4294883355U
uint x=rvec->x,c=rvec->y;//解包状态
uint res=x^c;//计算结果
uint hi=mul_hi(x,A);//步进RNG
x=x*A+c;

c=hi+(xYes)。示例代码每次调用时都会生成相同的随机数序列

要获得不同的随机数流,只需以不同的方式初始化任意值k[1..3]和/或c[1..3]。您可以从命令行参数、环境变量、时间、保存状态、/dev/uradom或任何其他源对其进行初始化。请注意:

a) 如果您在两次不同的运行中以完全相同的方式初始化它们,那么这两次运行将得到相同的随机数流

b) 如果在两次不同的运行中对它们进行不同的初始化,那么这两次运行将得到不同的随机数流

有时你想要财产a)。有时你想要财产b)。花点时间想想你想要的是什么,并确保你在做你想做的事

更一般地说,库中的函数,例如threefry4x32,具有无状态。如果更改输入中的任何位(即c或k元素中的任何位),将获得完全不同的随机、统计独立、均匀分布的输出

顺便说一句,我是图书馆和《平行数字:简单到1,2,3》一文的作者之一:

如果您不是ACM数字图书馆的订户,上面的链接可能会碰到付费墙。或者,您也可以通过以下页面上的链接免费获取该论文:


0xdecafbad
0xfacebead
0x12345678
0xf00dcafe
0xdeadbeef
0xbeeff00d
只是任意选择的数字,它们并不特别。任何其他数字(甚至0)都可以用在它们的位置上——我将在示例代码中添加注释

您可以用传入的变量替换它们中的任何一个;避免输出随机“流”中不希望出现的重复的唯一要求是避免重复(c,k)输入元组。示例代码使用线程id和循环索引来确保唯一性,但您可以轻松添加更多变量以确保唯一性——例如,计算主机代码中的内核调用数并传入该计数器,使用该计数器代替k或c的一个元素


顺便说一下,尽管名称为“基于计数器的随机数生成器”,但不要求输入(c,k)作为“计数器”,只是计数器恰好是确保输入不重复的最方便的习惯用法。

阅读理论并比较一些代码,random123似乎建立在一些相同的原则上。我至少找到了腐烂和位移位的模式。据我所知,我认为random123它很聪明,因为不需要携带额外的随机数组。使用了两个循环不变量:三个fry4x32_key_t k={{tid,0xdecafbad,0xfacebead,0x12345678};三个fry4x32_ctr_t c={{0,0xf00dcafe,0xdeadbeef,0xbeeff00d};tid是全局id,0是工作项中的循环计数器。现在我猜最后传递的其他6个数字是种子。希望有人能确认这一点,以及是否只是传递一个包含6个数字的结构作为种子。好吧,不管怎样,因为所有工作项的内核在所有调用中都是相同的,如果你不为了保存随机状态,每次调用内核时,在每个工作项中都会得到相同的随机数序列。这可能没关系,但如果不想这样,每次都必须为每个工作项传入不同的数据。是的,我现在在random123库中唯一感到困惑的是6是否是数字s 0xdecafbad、0xfacebead、0x12345678和0xf00dcafe、0xdeadbeef、0xbeeff00d只是随机数(种子)或特殊数。这可能会有帮助,但很难阅读!你能试着将其分解一些吗?嗨,我知道这是一个边缘情况,但对于情况(b)你不也必须确保你没有意外地用计数器环绕吗?例如,如果你用{0,0,0}和{0,1,0,0}分别为一个流和另一个流播种,比如说