如何在pyCUDA内核中生成随机数?

时间:2017-09-12 06:43:38

标签: cuda pycuda

我正在使用pyCUDA进行CUDA编程。我需要在内核函数中使用随机数。 CURAND库在其中不起作用(pyCUDA)。因为,在GPU中有很多工作要做,在CPU内部生成随机数然后将它们转移到GPU将无法工作,而是解决了使用GPU的动机。

补充问题:

  1. 有没有办法在GPU上使用1个块和1个线程分配内存。
  2. 我使用的是多个内核。我需要使用多个SourceModule块吗?

2 个答案:

答案 0 :(得分:4)

尽管你在问题中断言,但PyCUDA对CUrand提供了相当全面的支持。 GPUArray模块具有直接接口,使用主机端API填充设备内存(注意在这种情况下随机生成器在GPU上运行)。

在PyCUDA内核代码中使用CUrand的设备端API也是完全可能的。在这个用例中,最棘手的部分是为线程生成器状态分配内存。有三种选择 - 静态代码,动态使用主机内存端分配,动态使用设备端内存分配。以下(非常轻微测试的)示例说明了后者,在您的问题中看到了它:

import numpy as np
import pycuda.autoinit
from pycuda.compiler import SourceModule
from pycuda import gpuarray

code = """
    #include <curand_kernel.h>

    const int nstates = %(NGENERATORS)s;
    __device__ curandState_t* states[nstates];

    __global__ void initkernel(int seed)
    {
        int tidx = threadIdx.x + blockIdx.x * blockDim.x;

        if (tidx < nstates) {
            curandState_t* s = new curandState_t;
            if (s != 0) {
                curand_init(seed, tidx, 0, s);
            }

            states[tidx] = s;
        }
    }

    __global__ void randfillkernel(float *values, int N)
    {
        int tidx = threadIdx.x + blockIdx.x * blockDim.x;

        if (tidx < nstates) {
            curandState_t s = *states[tidx];
            for(int i=tidx; i < N; i += blockDim.x * gridDim.x) {
                values[i] = curand_uniform(&s);
            }
            *states[tidx] = s;
        }
    }
"""

N = 1024
mod = SourceModule(code % { "NGENERATORS" : N }, no_extern_c=True, arch="sm_52")
init_func = mod.get_function("_Z10initkerneli")
fill_func = mod.get_function("_Z14randfillkernelPfi")

seed = np.int32(123456789)
nvalues = 10 * N
init_func(seed, block=(N,1,1), grid=(1,1,1))
gdata = gpuarray.zeros(nvalues, dtype=np.float32)
fill_func(gdata, np.int32(nvalues), block=(N,1,1), grid=(1,1,1))

这里有一个初始化内核需要运行一次,为发生器状态分配内存并用种子初始化它们,然后是使用这些状态的内核。如果要运行大量线程,则需要注意malloc堆大小限制,但可以通过PyCUDA驱动程序API接口对其进行操作。

答案 1 :(得分:-1)

我接受的答案有一个问题。 我们在那里有一个令人讨厌的名称(这些_Z10initkerneli_Z14randfillkernelPfi)。  为避免这种情况,我们可以手动将代码包装在extern "C" {...}子句中。

code = """
    #include <curand_kernel.h>

    const int nstates = %(NGENERATORS)s;
    __device__ curandState_t* states[nstates];
    extern "C" {

    __global__ void initkernel(int seed)
    { .... }

    __global__ void randfillkernel(float *values, int N)
    { .... }
    }
"""

然后,该代码仍使用no_extern_c=True进行编译:

mod = SourceModule(code % { "NGENERATORS" : N }, no_extern_c=True)

这应该与

一起使用
init_func = mod.get_function("initkernel")
fill_func = mod.get_function("randfillkernel")

希望有帮助。