如何在CUDA C中生成每次运行时具有不同种子的随机数?

如何在CUDA C中生成每次运行时具有不同种子的随机数?,cuda,nvidia,gpu,Cuda,Nvidia,Gpu,我正在研究一个随机过程,每次运行程序时,我都想在CUDA内核中生成不同的序列if随机数。 这与我们在C++中通过声明所做的类似 种子=时间(空) 然后是srand(seed) 兰特() 我可以通过内核将种子从主机传递到设备,但是 这样做的问题是,我必须将整个种子数组传递到内核中,以便每个线程每次都有一个不同的随机种子。有没有一种方法可以在内核中生成随机种子/进程if/机器时间或类似的东西,并将其作为种子传递?在每次运行时使用不同的种子应该很简单。确切的方法取决于您使用的生成器,但是如果您使用的是

我正在研究一个随机过程,每次运行程序时,我都想在CUDA内核中生成不同的序列if随机数。 这与我们在C++中通过声明所做的类似 种子=时间(空) 然后是srand(seed) 兰特()

我可以通过内核将种子从主机传递到设备,但是
这样做的问题是,我必须将整个种子数组传递到内核中,以便每个线程每次都有一个不同的随机种子。有没有一种方法可以在内核中生成随机种子/进程if/机器时间或类似的东西,并将其作为种子传递?

在每次运行时使用不同的种子应该很简单。确切的方法取决于您使用的生成器,但是如果您使用的是其中一个生成器,那么您可以将time\u t从time(NULL)转换为64位整数,并将其传递给种子函数

如果从内核调用生成器,则需要将此种子作为内核参数或通过
\uuu设备\uu
变量传入。然后,可以使用偏移量
curand\u init()
或使用
skip\u ahead()
获得不同的子序列


如果您有一个特定的生成器,但该生成器不起作用,请发布更多信息。

您不需要传递随机种子数组,但是,当您使用cuRAND库时,可以正确设置
cuRAND\u init
的序号参数。例如[免责声明:这是一个未经测试的功能]

__global__ void generate_random_numbers(float* numbers, unsigned long seed, int Np) {

    int i = threadIdx.x + blockIdx.x * blockDim.x;

    if (i < Np) {

        curandState state;

        curand_init(seed, i, 0, &state);

        numbers[i] = curand_uniform(&state);
    }
}
编辑

根据Roger Dahl的评论,我对生成
131072
元素数组的四种不同可能性进行了比较(开普勒K20c):

  • 单随机数生成:单独的内核用于初始化和随机数生成
  • 单随机数生成:用于初始化和随机数生成的唯一内核
  • 多重随机数生成:单独的内核用于初始化和随机数生成
  • 多重随机数生成:用于初始化和随机数生成的唯一内核 下面是代码。发电的时间安排如下:

  • 861ms
  • 852ms
  • 866ms
  • 2556ms
    我希望我已经正确理解了罗杰·达尔提出的性能问题

    #include <stdio.h>
    #include <curand.h>
    #include <curand_kernel.h>
    
    #define DSIZE 8192*16
    #define nTPB 256
    
    /***********************/
    /* CUDA ERROR CHECKING */
    /***********************/
    #define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
    inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
    {
        if (code != cudaSuccess) 
        {
            fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line); 
            if (abort) exit(code);
        }
    }
    
    /*************************/
    /* CURAND INITIALIZATION */
    /*************************/
    __global__ void initCurand(curandState *state, unsigned long seed){
        int idx = threadIdx.x + blockIdx.x * blockDim.x;
        curand_init(seed, idx, 0, &state[idx]);
    }
    
    __global__ void testrand1(curandState *state, float *a){
        int idx = threadIdx.x + blockIdx.x * blockDim.x;
        a[idx] = curand_uniform(&state[idx]);
    }
    
    __global__ void testrand2(unsigned long seed, float *a){
        int idx = threadIdx.x + blockIdx.x * blockDim.x;
        curandState state;
        curand_init(seed, idx, 0, &state);
        a[idx] = curand_uniform(&state);
    }
    
    /********/
    /* MAIN */
    /********/
    int main() {
    
        int n_iter = 20;
    
        curandState *devState;  gpuErrchk(cudaMalloc((void**)&devState, DSIZE*sizeof(curandState)));
    
        float *d_a;             gpuErrchk(cudaMalloc((void**)&d_a, DSIZE*sizeof(float)));
    
        float time;
        cudaEvent_t start, stop;
    
        cudaEventCreate(&start);
        cudaEventCreate(&stop);
        cudaEventRecord(start, 0);
    
        for (int i=0; i<n_iter; i++) {
    
            initCurand<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, 1);
            gpuErrchk(cudaPeekAtLastError());
            gpuErrchk(cudaDeviceSynchronize());
    
            testrand1<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, d_a);
            gpuErrchk(cudaPeekAtLastError());
            gpuErrchk(cudaDeviceSynchronize());
    
        }
    
        cudaEventRecord(stop, 0);
        cudaEventSynchronize(stop);
        cudaEventElapsedTime(&time, start, stop);
        printf("Elapsed time for separate kernels:  %3.1f ms \n", time);
    
        cudaEventRecord(start, 0);
    
        for (int i=0; i<n_iter; i++) {
    
            testrand2<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(1, d_a);
            gpuErrchk(cudaPeekAtLastError());
            gpuErrchk(cudaDeviceSynchronize());
    
        }
    
        cudaEventRecord(stop, 0);
        cudaEventSynchronize(stop);
        cudaEventElapsedTime(&time, start, stop);
        printf("Elapsed time for single kernels:  %3.1f ms \n", time);
    
        cudaEventRecord(start, 0);
    
        for (int i=0; i<n_iter; i++) {
    
            initCurand<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, 1);
            gpuErrchk(cudaPeekAtLastError());
            gpuErrchk(cudaDeviceSynchronize());
    
            testrand1<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, d_a);
            gpuErrchk(cudaPeekAtLastError());
            gpuErrchk(cudaDeviceSynchronize());
    
            testrand1<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, d_a);
            gpuErrchk(cudaPeekAtLastError());
            gpuErrchk(cudaDeviceSynchronize());
    
            testrand1<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(devState, d_a);
            gpuErrchk(cudaPeekAtLastError());
            gpuErrchk(cudaDeviceSynchronize());
    
        }
    
        cudaEventRecord(stop, 0);
        cudaEventSynchronize(stop);
        cudaEventElapsedTime(&time, start, stop);
        printf("Elapsed time for separate kernels with multiple random number generation:  %3.1f ms \n", time);
    
        cudaEventRecord(start, 0);
    
        for (int i=0; i<n_iter; i++) {
    
            testrand2<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(1, d_a);
            gpuErrchk(cudaPeekAtLastError());
            gpuErrchk(cudaDeviceSynchronize());
    
            testrand2<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(1, d_a);
            gpuErrchk(cudaPeekAtLastError());
            gpuErrchk(cudaDeviceSynchronize());
    
            testrand2<<<(DSIZE+nTPB-1)/nTPB,nTPB>>>(1, d_a);
            gpuErrchk(cudaPeekAtLastError());
            gpuErrchk(cudaDeviceSynchronize());
        }
    
        cudaEventRecord(stop, 0);
        cudaEventSynchronize(stop);
        cudaEventElapsedTime(&time, start, stop);
        printf("Elapsed time for single kernels for multiple random number generation:  %3.1f ms \n", time);
    
        getchar();
    }
    
    GTX570上的输出:

    单独内核的运行时间:957.2毫秒
    单内核运行时间:947.7毫秒
    生成多个随机数的独立内核的运行时间:964.6毫秒
    生成多个随机数的单内核运行时间:2839.0毫秒
    

    性能与K20c大致相同。

    您可以创建多个全局函数来初始化和生成随机数。或者创建一个循环来遍历全局函数 例子: 对于(int rns=0;rns<5;rns++){//too种子“循环”次数

        init << < N, 10 >> > (devState, time(0));
        gpuErrchk(cudaMalloc((void**)&gpu_no, N * sizeof(double))); // allocate memory for random numbers on device/GPU
    //rndn << < N, 10 >> > (devState, gpu_no);//invoke kernel to launch the random numbers
        gpuErrchk(cudaMemcpy(cpu_no, gpu_no, N * sizeof(double), cudaMemcpyDeviceToHost))
    } cout << "the transition matrix  " << ++generate << " seed generation is:  " << init << endl;
    
    init>>(devState,时间(0));
    gpuErrchk(cudamaloc((void**)和gpu_no,N*sizeof(double));//为设备/gpu上的随机数分配内存
    //rndn>>(devState,gpu_no);//调用内核以启动随机数
    gpuerchk(cudaMemcpy(cpu_编号,gpu_编号,N*sizeof(双精度),cudaMemcpyDeviceToHost))
    
    }到目前为止,你们的代码是什么?我目前正在使用curand_uniform()对于随机数生成,但它每次运行时都调用同一组随机数,我可以通过内核将种子从主机传递到设备,但这样做的问题是,我必须将整个种子数组传递到内核中,以便每个线程每次都有不同的随机种子。有没有一种方法可以在内核中生成随机种子/进程if/机器时间或类似的东西,并将其作为种子传递?您可以使用单个主种子,然后根据线程索引偏移到序列中。请注意,仅对同一个生成器使用不同的种子在统计上并不可靠,您应该使用单个种子,然后偏移到序列中以获得不同的子序列。这样做的问题是,我必须将整个种子数组传递到内核中,以便每个线程每次都具有不同的随机种子。有没有一种方法可以在内核中生成随机种子/进程if/机器时间或类似的东西,并将其作为种子传递?没有,可以传入单个种子,然后在序列中使用偏移量。无论如何,您不应该只使用不同的种子,这种方法容易导致序列之间的相关性。处理prng种子的不同方法会对性能产生影响。从curand文档中的性能说明部分:对curand_init()的调用比对curand()或curand_uniform()的调用慢。到curand_init()的大偏移量比小偏移量花费更多的时间。保存和恢复随机生成器状态要比反复重新计算开始状态快得多。@RogerDahl我已经编辑了我的答案。我希望我正确理解了您提出的性能问题。感谢您调查性能问题。我认为从文档中的注释中得到的主要信息是,如果可能的话,将PRNG的种子转移到循环之外可能是一个好主意。因此,如果您正在运行一个模拟,其中您一直启动一个生成随机数的内核,那么您将为每个线程创建一个运行
    curand_init()
    的内核,并保存状态。当应用程序启动时,您只运行该内核一次。然后,在循环中,您只需继续恢复和保存状态。@RogerDahl感谢您将自己的结果添加到我的答案中。我认为上述结果与您最后的评论一致,在这种情况下#3的性能明显优于#4。
        init << < N, 10 >> > (devState, time(0));
        gpuErrchk(cudaMalloc((void**)&gpu_no, N * sizeof(double))); // allocate memory for random numbers on device/GPU
    //rndn << < N, 10 >> > (devState, gpu_no);//invoke kernel to launch the random numbers
        gpuErrchk(cudaMemcpy(cpu_no, gpu_no, N * sizeof(double), cudaMemcpyDeviceToHost))
    } cout << "the transition matrix  " << ++generate << " seed generation is:  " << init << endl;