我最近正在研究一种代码,该代码需要在每次内核启动之前初始化一段全局内存,稍后将在同一内核中对其进行修改。我曾经在每个内核启动之前做cudaMemset
。但是当我需要调用这个内核数千次时,开销不能忽略。所以我最终想出了这个想法,即使用全局内存判断是否所有初始化工作都已完成。但是我很快发现,当活动块中的某些线程正在进行循环时,以下块将不会继续启动,这会导致死循环。
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < (n + n)) {
data[i] = 0;
}//working.
__syncthreads();//sync
if (threadIdx.x == 0) {
atomicAdd((unsigned *)&flag, 1);//voting
while (flag < gridDim.x); //waiting
}
}
__syncthreads();
//do something with data
那么有没有办法手动将当前块置于休眠状态并保持内核启动?或者我的初始化问题有更好的解决方案吗?
答案 0 :(得分:4)
正如您所发现的,您不应该在CUDA中尝试阻止同步 - 这将阻止以后的块启动(因为早期的块不会放弃它们的资源)和同步点的死锁。
尝试将工作移动到恰好正在运行的块,而不是尝试将块放入休眠状态。 Programming Guide有一个worked example at the end of it's memory fence section用于在内核的最后一个块中做一些额外的工作。您可以使用它来为下一个块准备全局内存变量。
不需要执行额外的cudaMemcpy()或额外的内核启动的好处需要权衡每个块的额外原子内存访问和每个块内的同步。因此,在某个时刻每个网格的块数越来越多,只需执行额外的cudaMemcpy()就会更便宜。