如何在OpenCL中基于数据同步(特定)工作项? 背景:

如何在OpenCL中基于数据同步(特定)工作项? 背景:,opencl,gpgpu,Opencl,Gpgpu,需要模拟一组相关的离散元件(复杂电子电路)。因此,每个组件从其他几个组件接收输入,并输出到其他几个组件 预期的设计是有一个内核,配置参数定义了它应该表示的组件。电路的每个组件都由一个工作项表示,并且所有电路都将适合于单个工作组(或者将电路进行适当的拆分,以便每个工作组可以将所有组件作为工作项进行管理) 问题是: 有可能吗?万一有,怎么可能?是否让某些工作项等待其他工作项数据? 工作项(在数据驱动位置)生成到阵列的输出。另一个工作项需要等待这种情况发生,然后才能开始进行处理。 网络没有循环,因此,

需要模拟一组相关的离散元件(复杂电子电路)。因此,每个组件从其他几个组件接收输入,并输出到其他几个组件

预期的设计是有一个
内核
,配置参数定义了它应该表示的组件。电路的每个组件都由一个工作项表示,并且所有电路都将适合于单个工作组(或者将电路进行适当的拆分,以便每个工作组可以将所有组件作为工作项进行管理)

问题是: 有可能吗?万一有,怎么可能?是否让某些工作项等待其他工作项数据? 工作项(在数据驱动位置)生成到阵列的输出。另一个工作项需要等待这种情况发生,然后才能开始进行处理。 网络没有循环,因此,单个工作项不可能需要运行两次

尝试: 在以下示例中,每个元件最多可以有一个单一输入(以简化),使电路成为一棵树,其中电路的输入为根,3个输出为叶

inputIndex
通过为每个组件指示哪个其他组件提供输入,对该树进行建模。第一个组件将自身作为输入,但内核管理这种情况(为了简化)

result
保存每个组件的结果(电压、强度等)

inputModified
指示给定组件是否已计算其输出

// where the data come from (index in result)
constant int inputIndex[5]={0,0, 0, 2, 2};

kernel void update_component(
    local int *result, // each work-item result. 
    local int *inputModified // If all inputs are ready (one only for this example)
) {

    int id = get_local_id(0);
    int size = get_local_size(0);
    int barrierCount = 0;

    // inputModified is a boolean indicating if the input is ready
    inputModified[id]=(id!=0 ? 0 : 1);

    // make sure all input are false by default (except the first input).
    barrier(CLK_LOCAL_MEM_FENCE); 


    // Wait until all inputs are ready (only one in this example)
    while( !inputModified[inputIndex[id]] && size > barrierCount++)
    {
        // If the input is not ready, wait for it
        barrier(CLK_LOCAL_MEM_FENCE);
    }

    // all inputs are ready, compute output
    if (id!=0) result[id] = result[inputIndex[id]]+1;
    else result[0]=42;

    // make sure any other work-item depending on this is unblocked
    inputModified[id]=1;

    // Even if finished, we needs to "barrier" for other working items.
    while (size > barrierCount++)
    {
        barrier(CLK_LOCAL_MEM_FENCE);
    }
}
本例中N个组件有N个屏障,这比顺序解决方案更糟糕

注:这只是内核,因为C++最小主机相当长。如果需要的话,我可以想办法添加它

问题: 是否可以通过内核本身高效地让不同的工作项等待其他工作项提供它们的数据?或者哪种解决方案更有效?


这个问题(对我来说)很难解释,而且我还远不是OpenCL方面的专家。请耐心等待,并随时询问是否有任何不清楚的地方。

来自屏障文档

如果屏障位于循环内,则所有工作项必须在允许任何工作项继续之前为循环的每次迭代执行屏障 超越障碍的执行

但是内核中的while循环(包含屏障)具有以下条件:

inputModified[inputIndex[id]]
这可能会更改其id为线程的行为,并导致未定义的行为。此外,这之前还有一个障碍

barrier(CLK_LOCAL_MEM_FENCE);
已经同步了工作组中的所有工作项,因此while循环是冗余的,即使它可以工作

最后一个屏障回路也是冗余的

while (size > barrierCount++)
{
    barrier(CLK_LOCAL_MEM_FENCE);
}
当内核结束时,它会同步所有工作项

如果您打算向工作组外的工作项发送一些消息,那么您只能使用原子变量。即使在使用原子时,也不应在任何两个工作项之间假设任何工作/发布顺序

你的问题

怎么做?是否让某些工作项等待其他工作项数据?A. 工作项生成到阵列的输出(在数据驱动位置)。 另一个工作项需要等待这种情况发生后才能开始 使之成为一种加工方式。网络没有环路,因此,这是不可能的 单个工作项需要运行两次

可以用OpenCL2.x的“动态并行”特性来回答这个问题,该特性允许工作项在内核中生成新的工作组/内核。它比在自旋等待循环中等待要高效得多,并且比依赖GPU支持的正在运行的线程数量(当GPU不能处理那么多正在运行的线程时,任何自旋等待都会死锁,线程的顺序无关紧要)

当您使用barrier时,不需要通知其他线程“inputModified”。结果的数据在工作组中已可见

如果不能使用OpenCL v2.x,则应使用BFS处理树:

  • 为顶部节点启动1个工作项
  • 处理它,准备K个输出,并将它们放入队列
  • 端核
  • 启动K个工作项(队列中的每个pop元素)
  • 处理它们,准备N个输出,并将它们推入队列
  • 端核
  • 重复此操作,直到队列中不再有任何元素
内核调用的数量等于树的最大深度,而不是节点的数量

如果您需要比“内核启动”更快的同步,那么对整个树使用单个工作组,使用barrier而不是内核回调。或者,在CPU上处理前几个步骤,拥有多个子树并将它们发送到不同的OpenCL工作组。也许在CPU上计算直到有N个子树,其中N=GPU的计算单元对于基于工作组屏障的更快的子树异步计算可能更好

还有一种无障碍、无原子和单内核的调用方式。从树的底部开始往上爬

将所有最深级别的子节点映射到工作项。将它们中的每一个移动到顶部,同时在它们的私有内存/其他一些快速内存中记录它们的路径(节点id等)。然后让他们自上而下穿过记录的路径,移动计算,没有任何同步,甚至没有原子。这比barrier/内核调用版本的工作效率要低,但是缺少barrier和完全异步的路径应该使它足够快

若树有10个深度,这就意味着要保存10个节点指针,而私有寄存器就不需要这么多了。如果树深度约为30 40,则使用本地内存,每个工作组中的线程数较少;如果更多,则分配全局内存

但是,您可能需要根据工作项的空间/树拓扑对其进行排序,以使它们以更少的分支更快地协同工作

这种方式在我看来很简单,所以我建议你先试试这个无障碍版本

如果你只想