OpenCL的;全局记忆同步;

时间:2014-03-28 10:13:36

标签: synchronization opencl gpu

我需要同步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,则此内核运行良好。 当我试图改变尺寸时,我会获得成功。

可能是什么原因?

1 个答案:

答案 0 :(得分:0)

GPU设备上的OpenCL使用 SIMD 执行(单指令,多数据)。 这意味着组中的所有工作线程将始终执行相同的代码,因为HW不允许以任何其他方式使用它。

在这个SIMD的情况下,不可能将“break”,“for”,“while”等...或任何未遇到相同次数的循环操作通过组中的所有工作线程。

由于您的代码明确需要只有1个线程退出循环,因此它永远不会工作。它将进入死锁状态,至少在SIMD设备上(所有存在的GPU)。

唯一的方法是,每个WG只有 1 WI (非常低效),或者使用CPU。您的问题无法以任何其他方式得到解答, OpenCL无法满足您的需求。没有技巧或代码可以解决您的问题。你必须改变你正在使用的算法才能使它真正平行。

注意:使用1x1 WG或printf()将执行序列化,这可以解决您的问题。但在这种情况下,根本没有使用GPU的意义。