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