【问题标题】:CUDA's Mersenne Twister for an arbitrary number of threadsCUDA 的 Mersenne Twister 用于任意数量的线程
【发布时间】:2013-10-30 07:22:59
【问题描述】:

CUDA 对Mersenne Twister (MT) 随机数生成器的实现被限制为256200 块/网格的最大线程/块数,即最大线程数为51200 .

因此,无法启动使用 MT 的内核

kernel<<<blocksPerGrid, threadsPerBlock>>>(devMTGPStates, ...)

在哪里

int blocksPerGrid = (n+threadsPerBlock-1)/threadsPerBlock;

n 是线程总数。

MT 用于threads &gt; 51200 的最佳方式是什么?

如果对blocksPerGridthreadsPerBlock 使用常量值,我的方法是,例如&lt;&lt;&lt;128,128&gt;&gt;&gt; 并在内核代码中使用以下代码:

__global__ void kernel(curandStateMtgp32 *state, int n, ...) { 

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

    while (id < n) {

        float x = curand_normal(&state[blockIdx.x]);
        /* some more calls to curand_normal() followed
           by the algorithm that works with the data */

        id += blockDim.x*gridDim.x; 
    }
}

我不确定这是否是正确的方式,或者它是否会以不希望的方式影响 MT 状态?

谢谢。

【问题讨论】:

    标签: random cuda mersenne-twister curand


    【解决方案1】:

    我建议你仔细阅读 CURAND documentation

    当每个块使用 256 个线程和最多 64 个块来生成数字时,MT API 效率最高。

    如果您需要更多,您有多种选择:

    1. 只需从现有状态集生成更多数字(即 64 块,256 个线程),并将这些数字分配给 需要它们的线程。
    2. 每个块使用多个状态(但这不允许您超过状态集中的总体限制,它只是解决了对单个块的需求。)
    3. 创建多个具有独立种子(因此也具有独立状态集)的 MT 生成器。

    一般来说,我认为您所概述的内核没有问题,它与上面的选择 1 大致一致。但是,它不允许您超过 51200 个线程。 (您的示例有 &lt;&lt;&lt;128, 128&gt;&gt;&gt; 所以 16384 个线程)

    【讨论】:

    • 感谢您的回答。同时我发现例如线程 0 的结果与线程 16384 的结果相同,依此类推,因此我的想法不适用于许多线程。我会思考你的建议以及如何将它们应用到我的程序中。顺便说一句:根据文档,我得出结论,同时将 XORWOW 用于 1e6 线程(即 1e6 状态)应该没有问题。还是有任何可能的限制?
    • 我所知道的这种类型的唯一限制是 MT。 MRG 和 XORWOW 不应该有这些类型的限制。
    【解决方案2】:

    根据 Robert 的回答,下面我将提供一个完整的示例,说明将 cuRAND 的 Mersenne Twister 用于任意数量的线程。我正在使用 Robert 的第一个选项从现有状态集中生成更多数字,并将这些数字分配给需要它们的线程。

    // --- Generate random numbers with cuRAND's Mersenne Twister
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #include <cuda.h>
    #include <curand_kernel.h>
    /* include MTGP host helper functions */
    #include <curand_mtgp32_host.h>
    
    #define BLOCKSIZE   256
    #define GRIDSIZE    64
    
    /*******************/
    /* GPU ERROR CHECK */
    /*******************/
    #define gpuErrchk(x) do { if((x) != cudaSuccess) { \
        printf("Error at %s:%d\n",__FILE__,__LINE__); \
        return EXIT_FAILURE;}} while(0)
    
    #define CURAND_CALL(x) do { if((x) != CURAND_STATUS_SUCCESS) { \
        printf("Error at %s:%d\n",__FILE__,__LINE__); \
        return EXIT_FAILURE;}} while(0)
    
    /*******************/
    /* iDivUp FUNCTION */
    /*******************/
    __host__ __device__ int iDivUp(int a, int b) { return ((a % b) != 0) ? (a / b + 1) : (a / b); }
    
    /*********************/
    /* GENERATION KERNEL */
    /*********************/
    __global__ void generate_kernel(curandStateMtgp32 * __restrict__ state, float * __restrict__ result, const int N)
    {
        int tid = threadIdx.x + blockIdx.x * blockDim.x;
        for (int k = tid; k < N; k += blockDim.x * gridDim.x)
            result[k] = curand_uniform(&state[blockIdx.x]);
    }
    
    /********/
    /* MAIN */
    /********/
    int main()
    {
        const int N = 217 * 123;
    
        // --- Allocate space for results on host
        float *hostResults = (float *)malloc(N * sizeof(float));
    
        // --- Allocate and initialize space for results on device 
        float *devResults; gpuErrchk(cudaMalloc(&devResults, N * sizeof(float)));
        gpuErrchk(cudaMemset(devResults, 0, N * sizeof(float)));
    
        // --- Setup the pseudorandom number generator
        curandStateMtgp32 *devMTGPStates; gpuErrchk(cudaMalloc(&devMTGPStates, GRIDSIZE * sizeof(curandStateMtgp32)));
        mtgp32_kernel_params *devKernelParams; gpuErrchk(cudaMalloc(&devKernelParams, sizeof(mtgp32_kernel_params)));
        CURAND_CALL(curandMakeMTGP32Constants(mtgp32dc_params_fast_11213, devKernelParams));
        //CURAND_CALL(curandMakeMTGP32KernelState(devMTGPStates, mtgp32dc_params_fast_11213, devKernelParams, GRIDSIZE, 1234));
        CURAND_CALL(curandMakeMTGP32KernelState(devMTGPStates, mtgp32dc_params_fast_11213, devKernelParams, GRIDSIZE, time(NULL)));
    
        // --- Generate pseudo-random sequence and copy to the host
        generate_kernel << <GRIDSIZE, BLOCKSIZE >> >(devMTGPStates, devResults, N);
        gpuErrchk(cudaPeekAtLastError());
        gpuErrchk(cudaDeviceSynchronize());
        gpuErrchk(cudaMemcpy(hostResults, devResults, N * sizeof(float), cudaMemcpyDeviceToHost));
    
        // --- Print results
        //for (int i = 0; i < N; i++) {
        for (int i = 0; i < 10; i++) {
            printf("%f\n", hostResults[i]);
        }
    
        // --- Cleanup
        gpuErrchk(cudaFree(devMTGPStates));
        gpuErrchk(cudaFree(devResults));
        free(hostResults);
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2014-02-22
      • 2017-04-01
      • 2014-04-17
      • 1970-01-01
      • 2020-01-10
      • 1970-01-01
      • 2017-06-10
      • 2017-08-20
      • 2014-05-20
      相关资源
      最近更新 更多