我一直在查看此lib Random123及其相关引用:
一位神秘男子来到我的展位,询问我对使用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;
}
我现在的问题是,每次运行时这不会生成相同的随机数,随机数是基于全局ID吗?我怎样才能每次生成新的随机数。可以提供种子作为内核的参数,然后以某种方式使用它吗?
任何使用过此lib的人都可以对我的使用有更深入的了解吗?
答案 0 :(得分:4)
是。示例代码每次调用时都会生成相同的随机数序列。
要获得不同的随机数流,只需以不同方式初始化任何值k [1..3]和/或c [1..3]。您可以从命令行参数,环境变量,时间,已保存状态,/ dev / urandom或任何其他源初始化它们。请注意:
a)如果你在两个不同的运行中以完全相同的方式初始化它们,那么这两个运行将获得相同的随机数流
b)如果你在两个不同的运行中以不同方式初始化它们,那么这两个运行将获得不同的随机数流。
有时你想要财产a)。有时你想要财产b)。花点时间考虑一下你想要的东西,并确保你正在做你想做的事。
更一般地说,库中的函数(例如threefry4x32)具有无状态。如果更改输入中的任何位(即c或k的任何元素中的任何位),您将获得完全不同的随机,统计独立,均匀分布的输出。
P.S。我是该图书馆的作者之一,并撰写了文章“平行数:简单如1,2,3”: http://dl.acm.org/citation.cfm?id=2063405
如果您不是ACM数字图书馆的订阅者,则上面的链接可能会打到付费墙。或者,您可以按照此页面上的链接免费获取论文:
答案 1 :(得分:2)
我本身无法帮助您使用库,但我可以告诉您,在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;
}
。 。 。然后,你要做的就是传递一个额外的数组,将RNG的状态保持为随机状态。实际上,您需要为每个工作项以不同方式为此数组设置种子。
答案 2 :(得分:1)
0xdecafbad
,0xfacebead
,0x12345678
和0xf00dcafe
,0xdeadbeef
,0xbeeff00d
只是任意选择的数字,它们并不特殊。任何其他数字(甚至0)都可以在他们的位置使用 - 我将在示例代码中添加注释。
您可以使用传入的变量替换其中任何一个;避免输出随机“流”中不希望的重复的唯一要求是避免重复(c,k)输入元组。示例代码使用线程id和循环索引来确保唯一性,但您可以轻松添加更多变量以确保唯一性 - 例如计算主机代码中的内核调用并传入该计数器,用它代替k或c中的一个元素。
顺便说一句,尽管名称为'基于计数器的随机数生成器',但并不要求输入(c,k)为'计数器',只是计数器恰好是确保输入的最方便的习惯用语。不要重复。