我有一个内核“while”循环,在以下示例代码,其迭代地更新使用关于邻居 (仅一个邻居的信息的阵列的元件)。当在当前迭代中没有元素被改变时,该循环停止。不正确的同步的“while”循环(仅在释放模式存在的)
不幸的是,在线程的某些情况下,部分外出这个循环的过早(例如,如果他们忽略同步屏障)。 一些输入正确每次处理和其它输入(其中许多是)被不正确地每次 (即不存在随机因素)处理。奇怪的是,这个错误只在发布版本中发生,而Debug版本总是 工作正常。更准确地说,CUDA编译器选项“-G(GPU生成调试信息)”确定 处理是否正确。大小为32x32或更小的数组始终可以正确处理。
下面是一个示例代码:
__global__ void kernel(int *source, int size, unsigned char *result, unsigned char *alpha)
{
int x = threadIdx.x, y0 = threadIdx.y * 4;
int i, y;
__shared__ bool alpha_changed;
// Zero intermediate array using margins for safe access to neighbors
const int stride = MAX_SIZE + 2;
for (i = threadIdx.x + threadIdx.y * blockDim.x; i < stride * (stride + 3); i += blockDim.x * blockDim.y)
{
alpha[i] = 0;
}
__syncthreads();
for (int bit = MAX_BITS - 1; bit >= 0; bit--)
{
__syncthreads();
// Fill intermediate array with bit values from input array
alpha_changed = true;
alpha[(x + 1) + (y0 + 1) * stride] = (source[x + (y0 + 0) * size] & (1 << bit)) != 0;
alpha[(x + 1) + (y0 + 2) * stride] = (source[x + (y0 + 1) * size] & (1 << bit)) != 0;
alpha[(x + 1) + (y0 + 3) * stride] = (source[x + (y0 + 2) * size] & (1 << bit)) != 0;
alpha[(x + 1) + (y0 + 4) * stride] = (source[x + (y0 + 3) * size] & (1 << bit)) != 0;
__syncthreads();
// The loop in question
while (alpha_changed)
{
alpha_changed = false;
__syncthreads();
if (alpha[(x + 0) + (y0 + 1) * stride] != 0 && alpha[(x + 1) + (y0 + 1) * stride] == 0)
{
alpha_changed = true;
alpha[(x + 1) + (y0 + 1) * stride] = 1;
}
__syncthreads();
if (alpha[(x + 0) + (y0 + 2) * stride] != 0 && alpha[(x + 1) + (y0 + 2) * stride] == 0)
{
alpha_changed = true;
alpha[(x + 1) + (y0 + 2) * stride] = 1;
}
__syncthreads();
if (alpha[(x + 0) + (y0 + 3) * stride] != 0 && alpha[(x + 1) + (y0 + 3) * stride] == 0)
{
alpha_changed = true;
alpha[(x + 1) + (y0 + 3) * stride] = 1;
}
__syncthreads();
if (alpha[(x + 0) + (y0 + 4) * stride] != 0 && alpha[(x + 1) + (y0 + 4) * stride] == 0)
{
alpha_changed = true;
alpha[(x + 1) + (y0 + 4) * stride] = 1;
}
__syncthreads();
}
__syncthreads();
// Save result
result[x + (y0 + 0) * size + bit * size * size] = alpha[(x + 1) + (y0 + 1) * stride];
result[x + (y0 + 1) * size + bit * size * size] = alpha[(x + 1) + (y0 + 2) * stride];
result[x + (y0 + 2) * size + bit * size * size] = alpha[(x + 1) + (y0 + 3) * stride];
result[x + (y0 + 3) * size + bit * size * size] = alpha[(x + 1) + (y0 + 4) * stride];
__syncthreads();
}
}
// Run only 1 thread block, where size equals 64.
kernel <<< 1, dim3(size, size/4) >>> (source_gpu, size, result_gpu, alpha_gpu);
该样品内核预期的结果是阵列,其中每行只能包含连续的时间间隔的“1”值 。但是取而代之的是,我得到了一些行,其中“0”和“1”以某种方式交替出现。
此错误被再现于我的移动GPU的GeForce 740M(开普勒),在Windows 7 X64的SP1,在任CUDA 6.0或6.5, 使用Visual C++ 2012或2013我还可以提供样品的Visual Studio项目与样本输入数组(即不正确处理)。
我已经尝试了syncthreads(),fences和“volatile”限定符的不同配置,但仍然存在此错误 。
任何帮助表示赞赏。
我同意''alpha_changed'是问题的根源,但不是使用共享内存,而是建议使用'__any()'这样的warp-level内部来进行循环控制。 – ArchaeaSoftware 2015-03-19 14:43:05
@ArchaeaSoftware是的,那是什么意思,通过做适当的减少。我可以将它添加到答案中。请注意'__any()'不是块级的,所以你需要稍微减少一些。但是我认为只要写入共享内存的所有线程写入相同的值,结果都是明确的,所以简单的方法就可以工作。 – roeland 2015-03-19 20:59:03
感谢您提供这样快速而有用的回复。 “在将共享变量设置为false前做__syncthreads()”解决了这个问题。似乎只有两个__syncthreads()是必需的(在循环的开始和结束时)。使用__any()的想法对我的真实内核也有一定的潜力,但由于共享内存的大小和寄存器的数量不足,它会增加复杂性并可能降低性能。 – Triant 2015-03-19 22:49:10