我正在研究我的游戏项目(塔防)我试图使用共享内存计算所有crite和塔与JCuda之间的距离。对于每个塔,我用N threds运行1个区块,其中N等于地图上的生物数量。我计算给定块的所有crite和塔之间的距离,并将迄今为止最小的找到距离存储在块的共享内存中。我目前的代码如下:
extern "C"
__global__ void calcDistance(int** globalInputData, int size, int
critters, int** globalQueryData, int* globalOutputData) {
//shared memory
__shared__ float minimum[2];
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = blockIdx.y;
if (x < critters) {
int distance = 0;
//Calculate the distance between tower and criter
for (int i = 0; i < size; i++) {
int d = globalInputData[x][i] - globalQueryData[y][i];
distance += d * d;
}
if (x == 0) {
minimum[0] = distance;
minimum[1] = x;
}
__syncthreads();
if (distance < minimum[0]) {
minimum[0] = distance;
minimum[1] = x;
}
__syncthreads();
globalOutputData[y * 2] = minimum[0];
globalOutputData[y] = minimum[1];
}
}
问题是我多次使用相同的输入重新运行代码(每次运行后我释放主机和设备上的所有内存)每次执行代码时都会得到不同的输出对于块(塔)编号&gt; 27 ...我相当肯定它与共享内存和处理它的方式有关,因为重写代码以使用全局内存会在代码执行时产生相同的结果。有什么想法吗?
答案 0 :(得分:1)
这个内核中存在内存竞争问题(所以写后读写正确性):
if (distance < minimum[0]) {
minimum[0] = distance;
minimum[1] = x;
}
执行时,块中的每个线程都将尝试同时读取和写入minimum的值。当warp中的多个线程尝试写入相同的共享内存位置时,无法保证会发生什么,并且无法保证在从正在写入的内存位置加载时,同一块中的其他warp将读取哪些值。内存访问不是原子的,并且没有锁定或序列化可以确保代码执行您似乎尝试执行的还原操作类型。
相同问题的较温和版本适用于在内核末尾写回全局内存:
__syncthreads();
globalOutputData[y * 2] = minimum[0];
globalOutputData[y] = minimum[1];
写入之前的屏障确保写入最小值将在“最终”(尽管不一致)值存储到最小值之前完成,但随后块中的每个线程都将执行写入。
如果您的目的是让每个线程计算一个距离,然后将块上的距离值的最小值写入全局内存,则必须使用原子内存操作(对于共享内存,这是仅支持计算1.2 / 1.3和2.x设备),或写入显式共享内存减少。之后,只有一个线程应该执行写回全局内存。
最后,您还有一个可能导致内核挂起的潜在同步正确性问题。 __syncthreads()
(映射到PTX条指令)要求块中的每个线程到达并在内核继续之前执行指令。拥有这种控制流程:
if (x < critters) {
....
__syncthreads();
....
}
如果块中的某些线程可以围绕屏障分支并退出而其他线程在屏障处等待,则将导致内核挂起。在__syncthreads()调用周围不应该存在任何分支差异,以确保CUDA中内核的执行正确性。
总而言之,回到当前代码中至少有三个问题的绘图板。