How to get a “random” number in OpenCL

后端 未结 9 839
悲&欢浪女
悲&欢浪女 2020-12-07 22:59

I\'m looking to get a random number in OpenCL. It doesn\'t have to be real random or even that random. Just something simple and quick.

I see there is a ton of rea

相关标签:
9条回答
  • 2020-12-07 23:28

    I was solving this "no random" issue for last few days and I came up with three different approaches:

    1. Xorshift - I created generator based on this one. All you have to do is provide one uint2 number (seed) for whole kernel and every work item will compute his own rand number

      // '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 - I used code from .next(int bits) method to generate random number. This time you have to provide one ulong number as seed.

      // 'randoms' is ulong passed to kernel
      ulong seed = randoms + globalID;
      seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
      uint result = seed >> 16;
      
    3. Just generate all on CPU and pass it to kernel in one big buffer.

    I tested all three approaches (generators) in my evolution algorithm computing Minimum Dominating Set in graphs.

    I like the generated numbers from the first one, but it looks like my evolution algorithm doesn't.

    Second generator generates numbers that has some visible pattern but my evolution algorithm likes it that way anyway and whole thing run little faster than with the first generator.

    But the third approach shows that it's absolutely fine to just provide all numbers from host (cpu). First I though that generating (in my case) 1536 int32 numbers and passing them to GPU in every kernel call would be too expensive (to compute and transfer to GPU). But it turns out, it is as fast as my previous attempts. And CPU load stays under 5%.

    BTW, I also tried MWC64X Random but after I installed new GPU driver the function mul_hi starts causing build fail (even whole AMD Kernel Analyer crashed).

    0 讨论(0)
  • 2020-12-07 23:29

    I am currently implementing a Realtime Path Tracer. You might already know that Path Tracing requires many many random numbers.
    Before generating random numbers on the GPU I simply generated them on the CPU (using rand(), which sucks) and passed them to the GPU.
    That quickly became a bottleneck.
    Now I am generating the random numbers on the GPU with the Park-Miller Pseudorandom Number Generator (PRNG).
    It is extremely simple to implement and achieves very good results.
    I took thousands of samples (in the range of 0.0 to 1.0) and averaged them together.
    The resulting value was very close to 0.5 (which is what you would expect). Between different runs the divergence from 0.5 was around 0.002. Therefore it has a very uniform distribution.

    Here's a paper describing the algorithm:
    http://www.cems.uwe.ac.uk/~irjohnso/coursenotes/ufeen8-15-m/p1192-parkmiller.pdf
    And here's a paper about the above algorithm optimized for CUDA (which can easily be ported to OpenCL): http://www0.cs.ucl.ac.uk/staff/ucacbbl/ftp/papers/langdon_2009_CIGPU.pdf

    Here's an example of how I'm using it:

    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.
    }
    

    The code serves just as an example. I have not tested it.
    The array "seed_memory" is being filled with rand() only once before the first execution of the kernel. After that, all random number generation is happening on the GPU. I think it's also possible to simply use the kernel id instead of initializing the array with rand().

    0 讨论(0)
  • 2020-12-07 23:34

    why not? you could just write a kernel that generates random numbers, tough that would need more kernel calls and eventually passing the random numbers as argument to your other kernel which needs them

    0 讨论(0)
提交回复
热议问题