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

3

我最近发现了CUDA 5.0中的cuda-memcheck --tool racecheck工具,它包含了racecheck工具,详情请参见NVIDIA doc。该工具可以检测CUDA内核中共享内存的竞争条件。

在调试模式下,该工具不会检测到任何问题,这似乎是正常的。然而,在发布模式(-O3)下,根据问题的参数,我会得到错误信息。

以下是一个错误示例(第22行共享内存初始化,第119行赋值):

========= ERROR: Potential WAW hazard detected at __shared__ 0x0 in block (35, 0, 0) :
=========     Write Thread (32, 0, 0) at 0x00000890 in ....h:119:void kernel_test3<float, unsigned int=4, unsigned int=32, unsigned int=64>(Data<float, unsigned int=4, unsigned int=32, unsigned int=64>*)
=========     Write Thread (0, 0, 0) at 0x00000048 in ....h:22:void kernel_test3<float, unsigned int=4, unsigned int=32, unsigned int=64>(Data<float, unsigned int=4, unsigned int=32, unsigned int=64>*)  
=========     Current Value : 13, Incoming Value : 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个线程)。我不明白为什么。

为了理解发生了什么,我测试了一些更简单的内核。 让我展示一个创建这种错误的内核的例子。基本上,这个内核使用SIZE_X*SIZE_Y*NTHREADS*sizeof(float)字节的共享内存,而我可以每个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个线程 => 不会导致任何明显的竞争检查错误。

  • kernel_test<32><<<64, 32>>>(); :64个块,每个块32个线程 => 导致WAW危害(threadId.x = 32?!)和错误。

    ========= ERROR: Potential WAW hazard detected at __shared__ 0x6 in block (57, 0, 0) :  
    =========     Write Thread (0, 0, 0) at 0x00000048 in ....h:403:void kernel_test(void)  
    =========     Write Thread (1, 0, 0) at 0x00000048 in ....h:403:void kernel_test(void)  
    =========     Current Value : 0, Incoming Value : 128  
    
    ========= INFO:(Identical data being written) Potential WAW hazard detected at __shared__ 0x0 in block (47, 0, 0) :  
    =========     Write Thread (32, 0, 0) at 0x00000048 in ....h:403:void kernel_test(void)  
    =========     Write Thread (0, 0, 0) at 0x00000048 in ....h:403:void kernel_test(void)  
    =========     Current Value : 0, Incoming Value : 0  
    

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

更新:

NBLOCKS > 32时,问题似乎来自于cudaDeviceSynchronize()。为什么会发生这种情况?


嗨,BenC,你能发布一个完整的重现案例吗?另外,你在哪个GPU上运行它,CUDA驱动程序和工具包版本是什么? - Vyas
嗨,Vyas,通常我提供的最小代码就足以重现问题。你缺少什么信息?我的显卡是Geforce GT 650M,CUDA 5.0,CC 3.0和304.64驱动程序。 - BenC
嗨,BenC,你能发布完整的主机代码和设备代码以及你使用的确切构建行吗?这样可以更容易地重现问题。 - Vyas
@marina.k:NTHREADS 是每个块内的线程数,这里是32。 - BenC
@Vyas:我更新了我的帖子,发现这实际上是由于cudaDeviceSynchronize()对于某些块的原因。 - BenC
嗨,BenC,你能否更新到最新的可用驱动程序并重试你的应用程序?问题是否仍然存在? - Vyas
2个回答

2
首先,cudaDeviceSynchronize() 不是问题的根源;你的kernel才是问题所在,但它是异步调用,因此错误会在调用cudaDeviceSynchronize()时被捕获。
至于kernel,你的共享内存大小为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-/

我根据情况更新了代码。这确实是一个错误,因为我贴入了(错误的)复制代码。尽管修正后问题仍然存在。 - BenC
@BenC 嗯,这有点令人困惑。你是如何编译你的代码的?Racecheck工具不支持SM 1.x或3.5 http://docs.nvidia.com/cuda/cuda-memcheck/index.html#supported-devices - alrikai
我使用的是GT 650M,因此SM 3.0。然而,我根据NVIDIA论坛上vyas的建议再次尝试了最新的Linux NVIDIA驱动程序(313.18),错误似乎消失了。 - BenC

-2

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接