C++ cuda\uuuu syncthreads()在我的代码中不起作用

C++ cuda\uuuu syncthreads()在我的代码中不起作用,c++,cuda,C++,Cuda,情况是这样的 我有一个运行while循环的线程块,当且仅当这些线程中的任何一个满足某些条件时,我需要循环继续。为此,我使用一个共享变量作为继续标志,该标志在每次迭代开始时由线程#0清除,然后是一个\uu syncthreads(),如果满足继续条件,可以由迭代期间的任何线程设置。然后,在下一次迭代的检查点之前放置另一个对\uuu syncthreads()的调用,以确保线程同步。内核基本上是这样的: __global__ void foo(void* data) { __shared__

情况是这样的

我有一个运行while循环的线程块,当且仅当这些线程中的任何一个满足某些条件时,我需要循环继续。为此,我使用一个共享变量作为继续标志,该标志在每次迭代开始时由线程#0清除,然后是一个
\uu syncthreads()
,如果满足继续条件,可以由迭代期间的任何线程设置。然后,在下一次迭代的检查点之前放置另一个对
\uuu syncthreads()
的调用,以确保线程同步。内核基本上是这样的:

__global__ void foo(void* data) {
    __shared__ int blockContinueFlag;
    do {
        if (threadIdx.x || threadIdx.y || threadIdx.z) {
            blockContinueFlag = 0;
        }
        __syncthreads(); //synch1
        //some data manipulations...
        if(some predicate) {
            blockContinueFlag = true;
        }
        //some data manipulations...
        __syncthreads(); //synch2
    } while (blockContinueFlag);
}
__global__ void foo(void* data) {
  __shared__ int blockContinueFlag;
  blockContinueFlag = true;
  while (true) {
    if (!blockContinueFlag)
        break;
    if (threadIdx.x || threadIdx.y || threadIdx.z) {
        blockContinueFlag = 0;
    }
    __syncthreads(); //synch1
    //some data manipulations...
    if(some predicate) {
      blockContinueFlag = true;
    }
    //some data manipulations...
    __syncthreads(); //synch2
  };
问题是屏障synch2在我的代码中似乎不起作用,有时内核甚至在某些线程满足continue条件时终止(我通过检查主机端返回的数据知道这一点)。为了进一步检查这一点,我在do while循环之后设置了一个断点,就像下面的代码一样,其中有时
blockContinueFlag
表示
true
(我只能假设块在某些线程设置
blockContinueFlag
之前退出循环)

我记得从cuda手册中读到,如果谓词对所有线程的求值相同,则条件子句中允许使用
\u syncthreads()
,在本例中应该是这样

我有另一个简化版本的代码,只是为了说明这一点

__global__ void foo(int* data, int kernelSize, int threshold) {
    __shared__ int blockContinueFlag;
    do {
        if (threadIdx.x == 0) {
            blockContinueFlag = 0;
        }
        __syncthreads();
        if (threadIdx.x < kernelSize)  {
            data[threadIdx.x]--;
            for (int i = 0; i < threadIdx.x; i++);
            if (data[threadIdx.x] > threshold)
                blockContinueFlag = true;
        }
        __syncthreads();
    } while (blockContinueFlag);
}

int main()
{
    int hostData[1024], *deviceData;
    for (int i = 0; i < 1024; i++)
        hostData[i] = i;
    cudaMalloc(&deviceData, 1024 * sizeof(int));
    cudaMemcpy(deviceData, hostData, 1024 * sizeof(int), cudaMemcpyHostToDevice);
    foo << <1, 1024 >> >(deviceData, 512, 0);
    cudaDeviceSynchronize();
    cudaMemcpy(hostData, deviceData, 1024 * sizeof(int), cudaMemcpyDeviceToHost);
    fprintf(stderr, cudaGetErrorString(cudaGetLastError()));
    return 0;

}
,这表明扭曲实际上并没有同步

那么,是否有人知道原因和/或是否有办法让线程屏障正常工作


任何帮助都将不胜感激。提前感谢。

第一个示例是检查同步线程之间相同代码片段中的条件并清除标志。这是一种读后写的危险。 为了更好地举例说明你的问题,让我将你的例子改写如下:

__global__ void foo(void* data) {
    __shared__ int blockContinueFlag;
    do {
        if (threadIdx.x || threadIdx.y || threadIdx.z) {
            blockContinueFlag = 0;
        }
        __syncthreads(); //synch1
        //some data manipulations...
        if(some predicate) {
            blockContinueFlag = true;
        }
        //some data manipulations...
        __syncthreads(); //synch2
    } while (blockContinueFlag);
}
__global__ void foo(void* data) {
  __shared__ int blockContinueFlag;
  blockContinueFlag = true;
  while (true) {
    if (!blockContinueFlag)
        break;
    if (threadIdx.x || threadIdx.y || threadIdx.z) {
        blockContinueFlag = 0;
    }
    __syncthreads(); //synch1
    //some data manipulations...
    if(some predicate) {
      blockContinueFlag = true;
    }
    //some data manipulations...
    __syncthreads(); //synch2
  };
在本例中,对标志和循环中断的检查更加详细,但本质上是相同的代码(加上一开始的冗余检查)

在本例中,以及在您的代码中,线程0可能会在线程33(另一个扭曲)执行检查之前检查循环条件并清除标志。这导致了分歧,所有的邪恶都散开了


要解决此问题,您需要在清除标志之前添加另一个
\uuuuu syncthreads()

因此,我的解决方案是使用一个
\uuuu syncthreads\u或()
,而不是按要求添加三个
\uuu syncthreads()

__global__ void foo(void* data) {
    int blockContinueFlag;
    do {
        blockContinueFlag = 0;
        //some data manipulations...
        if(some predicate) {
            blockContinueFlag = true;
        }
        //some data manipulations...
    } while (__syncthreads_or(blockContinueFlag));
}
实际上,这比三个同步线程的速度稍快


再次感谢您的帖子。

这可能和一些编译器优化或//您编写的一些数据操作代码有关。尝试将循环的标志声明为
volatile\uuuuu shared\uuuu int blockContinueFlag
I'm with@pQB。这可能是由编译器优化引起的。你们用什么体系结构、构建标志和CUDA版本来实现这一点?谢谢你们,但我已经弄明白了。在线程0清除blockContinueFlag之前,应在每个迭代开始时添加另一个_syncthreads(),以确保线程0不会竞争到下一个迭代,并在其他线程检查之前清除该标志。实际上,我认为您已经迫使编译器“注意到”这个变量必须通过额外的同步由所有线程读/写。如果您试图将变量声明为volatile并附带结果,以及@talonmies询问的细节,那么有人可以给出一个高质量的答案。此外,您可能会在代码中引入更多真正需要的同步障碍。感谢您消除这些障碍。在我发布这个问题后,我也意识到了这一点。现在我使用的是一个uuSyncThreads或()而不是三个uuSyncThreads()。请使用
\uuuuSyncThreads或()
发布解决方案的代码。这似乎对其他人也有价值。在问题的第一段代码中,我没有看到相同的先读后写危害。你已经改变了做。。。while for while循环,因此检查条件的行(汇编代码应相同)。在问题代码中,在初始化
blockContinueFlag
之后,在检查谓词之前,所有线程都是同步的,然后,在更新标志之后和下一个循环迭代之前,线程再次同步。因此,我仍然认为存在一个积极的编译器优化问题,可以将共享变量声明为
volatile
。在最后一次u syncthreads()之后,所有线程都会执行以下操作:检查循环条件,将blockConfigureFlag归零。这是先读后写。我所做的只是使循环圆锥运动的检查更加明确。您需要在检查条件并清除标志后进行同步。注意,在整个计算过程中,它会消耗1个额外的寄存器。