我需要同步2D数组的计算。数组的一个元素是4x4 int的数据块。 每个块依赖于上面的块,如blk [Y] [X]应该在blk [Y-1] [X]之后计算。 我尝试使用全局地图进行同步。 Map是具有相应大小的volatile __global int的2D数组,因此在blk [Y] [X]之后,map [Y] [X]的内核设置值已经完成。
上面的每个内核检查标志都知道它可以像
一样开始__kernel
void kernel_blk_4x4(
__global __read_only uchar * __restrict _src,
__global __write_only uchar * __restrict _dst,
volatile __global int * __restrict map
){
int gidY = get_global_id(1);
int gidX = get_global_id(0);
// --- check flags before starting
volatile int kkk = 0;
volatile __global int * const map_ptr0 = map + (gidY)*31 + gidX;
volatile __global int * const map_ptr1 = map_ptr0 + 1;
volatile int val = *map_ptr0;
while(val == 0) {
kkk++;
val = *map_ptr0;
}
computation here...
volatile __global int *map_ptr = map + (gidY+1)*31 + gidX;
*map_ptr = 1;
}
对于第一行地图已经由1字段,所以在理论上它应该工作...
现实生活更有趣......
实际上,我陷入僵局。但是,如果我添加" printf();"代码中的某个地方,例如在while()之前或之后显示地图数组,一切正常......
我有什么想法吗?
谢谢你的帮助!
修改 同步已归档:)但是出现了另一个问题。 答:我改变了做法。每个线程扫描地图并采取一个块继续,因为只有一个准备就绪。请注意,8x8仅用于测试。
__global int *map_ptr = map;
int val = 0;
while (1) {
for(int y=0; y<8; y++) {
for(int x=0; x<8; x++) {
val = atomic_cmpxchg(map_ptr+x, 1, 2);
if(val == 1) {
map_ptr += x;
break;
}
}
if(val == 1) break;
map_ptr+=stride;
}
if(val == 1) break;
map_ptr = map;
}
// do some work
__global int *map_next = map_ptr+stride;
atomic_inc(map_next);
如果工作组大小为1x1,则此内核运行良好。 当我试图改变尺寸时,我会获得成功。
可能是什么原因?
答案 0 :(得分:0)
GPU设备上的OpenCL使用 SIMD 执行(单指令,多数据)。 这意味着组中的所有工作线程将始终执行相同的代码,因为HW不允许以任何其他方式使用它。
在这个SIMD的情况下,不可能将“break”,“for”,“while”等...或任何未遇到相同次数的循环操作通过组中的所有工作线程。
由于您的代码明确需要只有1个线程退出循环,因此它永远不会工作。它将进入死锁状态,至少在SIMD设备上(所有存在的GPU)。
唯一的方法是,每个WG只有 1 WI (非常低效),或者使用CPU。您的问题无法以任何其他方式得到解答, OpenCL无法满足您的需求。没有技巧或代码可以解决您的问题。你必须改变你正在使用的算法才能使它真正平行。
注意:使用1x1 WG或printf()将执行序列化,这可以解决您的问题。但在这种情况下,根本没有使用GPU的意义。