如何在OpenCL中获得“随机”数字

时间:2012-03-28 16:49:08

标签: opencl

我希望在OpenCL中获得一个随机数。它不必是真正随机的,甚至是随机的。只是一些简单快捷的东西。

我看到在OpenCL中有大量真正的随机并行花式裤子随机算法,就像成千上万行。我不需要那样的东西。一个简单的随机()'即使很容易看到模式,也没关系。

我看到有一个噪音功能?有没有简单的方法来获取随机数?

9 个答案:

答案 0 :(得分:16)

我在过去几天解决了这个“没有随机”的问题,我提出了三种不同的方法:

  1. Xorshift - 我根据这个创建了生成器。您所要做的就是为整个内核提供一个uint2个数字(种子),每个工作项都将计算自己的兰特数

    // 'randoms' is uint2 passed to kernel
    uint seed = randoms.x + globalID;
    uint t = seed ^ (seed << 11);  
    uint result = randoms.y ^ (randoms.y >> 19) ^ (t ^ (t >> 8));
    
  2. Java random - 我使用.next(int bits)方法中的代码生成随机数。这次你必须提供一个ulong号作为种子。

    // 'randoms' is ulong passed to kernel
    ulong seed = randoms + globalID;
    seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
    uint result = seed >> 16;
    
  3. 只需在CPU上生成所有内容并将其传递给一个大缓冲区中的内核。

  4. 我在我的进化算法中测试了所有三种方法(生成器),计算了图中的最小支配集。

    我喜欢第一个生成的数字,但看起来我的进化算法没有。

    第二个生成器生成具有一些可见模式的数字但是我的进化算法无论如何都喜欢它,并且整个事情比第一个生成器运行得快一点。

    但是第三种方法表明,只提供来自主机(cpu)的所有数字是绝对正确的。首先,我生成(在我的情况下)1536 int32数字并在每次内核调用中将它们传递给GPU将太昂贵(计算并转移到GPU)。但事实证明,它和我以前的尝试一样快。 CPU负载低于5%。

    BTW,我也试过MWC64X Random但是在安装新的GPU驱动程序后,函数mul_hi开始导致构建失败(甚至整个AMD内核分析程序崩溃)。

答案 1 :(得分:11)

以下是java.util.Random类根据doc使用的算法:

(seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)

请参阅文档了解其各种实现。将工人的身份传递给种子并循环几次应产生良好的随机性

或另一个方法是进行一些相当可以溢出的随机操作:

 long rand= yid*xid*as_float(xid-yid*xid);
 rand*=rand<<32^rand<<16|rand;
 rand*=rand+as_double(rand);

xid=get_global_id(0);yid= get_global_id(1);

答案 2 :(得分:11)

我目前正在实施实时路径追踪器。您可能已经知道路径跟踪需要许多随机数 在GPU上生成随机数之前,我只是在CPU上生成它们(使用rand(),这很糟糕)并将它们传递给GPU。
这很快就成了瓶颈 现在,我使用Park-Miller伪随机数生成器(PRNG)在GPU上生成随机数 实施起来非常简单,取得了很好的效果 我收集了数千个样本(范围为0.0到1.0)并将它们平均在一起 结果值非常接近0.5(这是您所期望的)。在不同的运行之间,0.5的差异大约为0.002。因此它具有非常均匀的分布
这是一篇描述算法的论文:
http://www.cems.uwe.ac.uk/~irjohnso/coursenotes/ufeen8-15-m/p1192-parkmiller.pdf
这里有一篇关于针对CUDA优化的上述算法的论文(可以很容易地移植到OpenCL):http://www0.cs.ucl.ac.uk/staff/ucacbbl/ftp/papers/langdon_2009_CIGPU.pdf

这是我如何使用它的一个例子:

int rand(int* seed) // 1 <= *seed < m
{
    int const a = 16807; //ie 7**5
    int const m = 2147483647; //ie 2**31-1

    *seed = (long(*seed * a))%m;
    return(*seed);
}

kernel random_number_kernel(global int* seed_memory)
{
    int global_id = get_global_id(1) * get_global_size(0) + get_global_id(0); // Get the global id in 1D.

    // Since the Park-Miller PRNG generates a SEQUENCE of random numbers
    // we have to keep track of the previous random number, because the next
    // random number will be generated using the previous one.
    int seed = seed_memory[global_id];

    int random_number = rand(&seed); // Generate the next random number in the sequence.

    seed_memory[global_id] = *seed; // Save the seed for the next time this kernel gets enqueued.
}

代码就是一个例子。我没有测试过。
在第一次执行内核之前,数组“seed_memory”只用rand()填充一次。之后,所有随机数生成都发生在GPU上。我认为也可以简单地使用内核id而不是用rand()初始化数组。

答案 3 :(得分:3)

似乎OpenCL没有提供这样的功能。但是,some people have done some research就可以了,并提供BSD许可代码,用于在GPU上生成良好的随机数。

答案 4 :(得分:2)

这是我的OpenCL float伪随机噪声版本,使用三角函数

//noise values in range if 0.0 to 1.0
static float noise3D(float x, float y, float z) {
    float ptr = 0.0f;
    return fract(sin(x*112.9898f + y*179.233f + z*237.212f) * 43758.5453f, &ptr);
}

__kernel void fillRandom(float seed, __global float* buffer, int length) {
    int gi = get_global_id(0);
    float fgi = float(gi)/length;
    buffer[gi] = noise3D(fgi, 0.0f, seed);
}

您可以通过将noise3D 规范化索引坐标作为第一个参数和随机种子(例如在CPU上生成)作为最后一个参数来生成1D或2D噪声。

以下是使用此内核生成的一些噪声图片和不同的种子:

noise1 noise2

答案 5 :(得分:1)

GPU没有良好的随机性来源,但可以通过使用来自主机的随机种子为内核播种来轻松克服这一问题。之后,您只需要一个可以处理大量并发线程的算法。

此链接描述了使用OpenCL的{Mersenne Twister实现:Parallel Mersenne Twister。您还可以在NVIDIA SDK中找到实现。

答案 6 :(得分:1)

我遇到了同样的问题。 www.thesalmons.org/john/random123/papers/random123sc11.pdf

您可以在此处找到文档。 http://www.thesalmons.org/john/random123/releases/latest/docs/index.html

您可以在此处下载图书馆: http://www.deshawresearch.com/resources_random123.html

答案 7 :(得分:0)

为什么不呢?你可以编写一个生成随机数的内核,这个内核需要更多内核调用,并最终将随机数作为参数传递给你需要它们的其他内核

答案 8 :(得分:-2)

你不能在内核中生成随机数,最好的选择是在主机(CPU)中生成随机数,然后通过缓冲区将其传输到GPU并在内核中使用它。