在cuda内核函数上实现互斥锁时发生了死锁

在cuda内核函数上实现互斥锁时发生了死锁,cuda,mutex,deadlock,Cuda,Mutex,Deadlock,我是cuda的新手,我尝试在内核函数中执行互斥 #include <iostream> #include <cuda_runtime.h> __global__ void countThreads(int* sum, int* mutex) { while(atomicCAS(mutex, 0, 1) != 0); // lock *sum += 1; __threadfence(); atomicExch(mutex, 0)

我是cuda的新手,我尝试在内核函数中执行互斥

#include <iostream>
#include <cuda_runtime.h>

__global__ void countThreads(int* sum, int* mutex) {
    while(atomicCAS(mutex, 0, 1) != 0); // lock
    
    *sum += 1;
    __threadfence();

    atomicExch(mutex, 0); // unlock
}

int main() {
    int* mutex = nullptr;
    cudaMalloc(&mutex, sizeof(int));
    cudaMemset(&mutex, 0, sizeof(int));

    int* sum = nullptr;
    cudaMalloc(&sum, sizeof(int));
    cudaMemset(&mutex, 0, sizeof(int));

    int ret = 0;
    // pass, result is 1024
    countThreads<<<1024, 1>>>(sum, mutex);
    cudaMemcpy(&ret, sum, sizeof(int), cudaMemcpyDeviceToHost);
    std::cout << ret << std::endl; 
    
    // deadlock, why?
    countThreads<<<1, 2>>>(sum, mutex);
    cudaMemcpy(&ret, sum, sizeof(int), cudaMemcpyDeviceToHost);
    std::cout << ret << std::endl;

    return 0;
}
我阅读了一些教程并编写了我的函数,但在某些情况下,出现了死锁

这是我的代码,内核函数非常简单,可以计算主函数启动的运行线程数

#include <iostream>
#include <cuda_runtime.h>

__global__ void countThreads(int* sum, int* mutex) {
    while(atomicCAS(mutex, 0, 1) != 0); // lock
    
    *sum += 1;
    __threadfence();

    atomicExch(mutex, 0); // unlock
}

int main() {
    int* mutex = nullptr;
    cudaMalloc(&mutex, sizeof(int));
    cudaMemset(&mutex, 0, sizeof(int));

    int* sum = nullptr;
    cudaMalloc(&sum, sizeof(int));
    cudaMemset(&mutex, 0, sizeof(int));

    int ret = 0;
    // pass, result is 1024
    countThreads<<<1024, 1>>>(sum, mutex);
    cudaMemcpy(&ret, sum, sizeof(int), cudaMemcpyDeviceToHost);
    std::cout << ret << std::endl; 
    
    // deadlock, why?
    countThreads<<<1, 2>>>(sum, mutex);
    cudaMemcpy(&ret, sum, sizeof(int), cudaMemcpyDeviceToHost);
    std::cout << ret << std::endl;

    return 0;
}
#包括
#包括
__全局无效countThreads(int*sum,int*mutex){
while(atomicCAS(互斥,0,1)!=0);//锁
*总和+=1;
__螺纹围栏();
atomicExch(互斥,0);//解锁
}
int main(){
int*mutex=nullptr;
cudamaloc(&mutex,sizeof(int));
cudaMemset(&mutex,0,sizeof(int));
int*sum=nullptr;
cudamaloc(和sum,sizeof(int));
cudaMemset(&mutex,0,sizeof(int));
int-ret=0;
//通过,结果是1024
countThreads(总和、互斥);
cudaMemcpy(&ret,sum,sizeof(int),cudamemcpydevicetoost);

std::cout同一扭曲中的线程尝试协商锁或互斥锁可能是最坏的情况。正确编程相当困难,并且行为可能会根据您运行的确切GPU而改变

是解释特定情况下死锁的确切原因所需的分析类型的一个示例。由于您没有指明要编译或运行的GPU的类型,因此无法对此处显示的内容进行此类分析。提供用于编译的CUDA版本也相当重要我已经见证了代码从一个编译器生成到另一个编译器的变化,这可能会影响到这一点。即使你提供了这些信息,我也不确定分析是否真的值得,因为我认为协商中的WARP情况对于程序的正确性是非常麻烦的。 我对CUDA新手的一般建议(如您所说)是使用类似于所述的方法。简单地说,在threadblock级别协商锁(即每个块中有一个线程在其他块之间协商锁)然后使用标准、可用的块级协调方案(如
\uuu syncthreads()
)和条件编码管理块内的单例活动

您可以通过在
cuda
标签上搜索诸如“lock”、“critical section”等关键字来了解有关此主题的更多信息

FWIW,无论如何,对我来说,您的代码在开普勒设备上会死锁,而在Volta设备上不会死锁,正如所建议的。我不会试图传达任何关于您的代码是否无缺陷的声明,这只是一个观察。如果我将您的内核修改为如下所示:

__global__ void countThreads(int* sum, int* mutex) {

    int old = 1;
    while (old){
      old = atomicCAS(mutex, 0, 1);  // lock
      if (old == 0){
        *sum += 1;
        __threadfence();

        atomicExch(mutex, 0); // unlock
        }
      }
}
在我看来,开普勒或沃尔特的例子都适用。我提出这个例子并不是为了说明它是“正确的”,而是为了表明某种无害的代码修改可以将代码从死锁状态更改为非死锁状态,或者反之亦然。我认为,最好避免这种脆弱性,当然是在伏尔塔之前的情况下

对于volta和forward的情况,CUDA 11和forward,您可能希望使用
libcu++
库中的功能,例如

请参见以“volta之前,…”开头的段落。