CUDA racecheck,共享内存数组和cudaDeviceSynchronize()

时间:2012-12-13 13:38:05

标签: cuda race-condition memcheck

我最近发现了 cuda-memcheck racecheck 工具,可在CUDA 5.0(cuda-memcheck --tool racecheck中找到,请参阅NVIDIA doc)。该工具可以在CUDA内核中使用共享内存检测竞争条件。

在调试模式下,此工具不会检测到任何内容,这显然是正常的。但是,在发布模式(-O3)中,我会根据问题的参数获得错误。

这是一个错误示例(第22行的共享内存初始化,第119行的分配):

  
    

=========错误:在块(35,0,0)中共享 0x0处检测到潜在的WAW危险:     =========将线程(32,0,0)写入0x00000890 ...... h:119:void kernel_test3(Data *)     =========将线程(0,0,0)写入0x00000048 ...... h:22:void kernel_test3(Data *)
    =========当前值:13,传入值:0

  
  1. 令我惊讶的第一件事是线程ID。当我第一次遇到错误时,每个块包含32个线程(ID为0到31)。那么为什么线程ID 32有问题?我甚至在threadIdx.x上添加了额外的检查,但这没有改变任何内容。
  2. 我使用共享内存作为临时缓冲区,并且每个线程处理其自己的多维数组参数,例如: __shared__ float arr[SIZE_1][SIZE_2][NB_THREADS_PER_BLOCK]。我真的不明白可能存在任何竞争条件,因为每个线程都处理它自己的共享内存部分。
  3. 将网格大小从64个块减少到32个块似乎解决了这个问题(每个块有32个线程)。我不明白为什么。
  4. 为了理解发生了什么,我测试了一些更简单的内核。 让我向您展示一个创建这种错误的内核示例。基本上,这个内核使用共享内存的SIZE_X*SIZE_Y*NTHREADS*sizeof(float) B,每个SM可以使用48KB的共享内存。

    test.cu

    template <unsigned int NTHREADS>
    __global__ void kernel_test()
    {
        const int SIZE_X = 4;
        const int SIZE_Y = 4;
    
        __shared__ float tmp[SIZE_X][SIZE_Y][NTHREADS];
    
        for (unsigned int i = 0; i < SIZE_X; i++)
            for (unsigned int j = 0; j < SIZE_Y; j++)
                tmp[i][j][threadIdx.x] = threadIdx.x;
    }
    
    int main()
    {
      const unsigned int NTHREADS = 32;
    
      //kernel_test<NTHREADS><<<32, NTHREADS>>>(); // ---> works fine
      kernel_test<NTHREADS><<<64, NTHREADS>>>();
    
      cudaDeviceSynchronize(); // ---> gives racecheck errors if NBLOCKS > 32
    }
    

    汇编

    nvcc test.cu --ptxas-options=-v -o test

    如果我们运行内核

    cuda-memcheck --tool racecheck test

    • kernel_test<32><<<32, 32>>>();:32个街区,32个网点=&gt;不会导致任何明显的竞争错误。
    • kernel_test<32><<<64, 32>>>();:64个块,32个线程=&gt;导致WAW危险(threadId.x = 32?!)和错误。
      
        

    =========错误:在块(57,0,0)中共享 0x6处检测到潜在的WAW危险:
        =========将线程(0,0,0)写入0x00000048 ...... h:403:void kernel_test(void)
        =========将线程(1,0,0)写入0x00000048 ...... h:403:void kernel_test(void)
        =========当前值:0,传入值:128

             

    ========= INFO :(正在写入的数据相同)在块(47,0,0)中共享 0x0处检测到潜在的WAW危险:
        =========将线程(32,0,0)写入0x00000048 ...... h:403:void kernel_test(void)
        =========将线程(0,0,0)写入0x00000048 ...... h:403:void kernel_test(void)
        =========当前值:0,传入值:0

      

    那我在这里错过了什么?我是否在共享内存方面做错了什么? (我还是初学者)

    **更新**

    cudaDeviceSynchronize()时,问题似乎来自NBLOCKS > 32。为什么会这样?

2 个答案:

答案 0 :(得分:2)

对于初学者来说,cudaDeviceSynchronize()不是原因;你的内核是原因,但它是一个异步调用,所以你调用cudaDeviceSynchronize()时会遇到错误。

对于内核,您的共享内存大小为SIZE_X * SIZE_Y * NTHREADS(在该示例中,每个块转换为512个元素)。在嵌套循环中,使用[i * blockDim.x * SIZE_Y + j * blockDim.x + threadIdx.x]将其编入索引 - 这就是您的问题所在。

更具体地说,你的i和j值的范围是[0,4],你的threadIdx.x是[0,32],你的SIZE_ {X | Y}值为4。 当blockDim.x为64时,循环中使用的最大索引将为991(从3 * 64 * 4 + 3 * 64 + 31)。当blockDim.x为32时,您的最大索引将为511。

根据您的代码,只要您的NBLOCKS超过NTHREADS

,就会出错

注意:我最初将其发布到https://devtalk.nvidia.com/default/topic/527292/cuda-programming-and-performance/cuda-racecheck-shared-memory-array-and-cudadevicesynchronize-/

答案 1 :(得分:0)

这显然是Linux的NVIDIA驱动程序中的一个错误。在313.18版本发布后,该漏洞消失了。