1

原则

我知道,如此简单的计算不值得精心并行化。就是这样一个例子,数学运算只是一些更有趣的计算的占位符。

[伪代码]

var id = 0,
do {
    id = getGlobalId();
    output[id] = input[id] * input[id];
} while (inRange(id) && output[id] !== 25);

最特殊的表达方式可能是:output[id] !== 25. 这意味着: 如果input有四个元素(按此顺序):[8, 5, 2, 9],那么output应该是[64, 25]2or的平方9不会被用作的项output(因为output[id] !== 25trueforid = 1input[id] = 5)。

如果您正在优化这段代码,您可能希望提前计算每个的平方input[id](不证明第二个while条件),但不能保证结果稍后是相关的(如果先前计算的结果是 25 ,当前计算的结果是无趣的)。

概括地说,我说的是计算结果 output[id]( output[id] = calculateFrom(input[id]);) 可能与每个都不相关的情况id——结果 ( output[id]) 的需要取决于另一个计算的结果。

我的目标

我想使用OpenCL内核和队列以尽可能并行和高性能的方式执行这个循环。

我的想法

  • 我想:为了能够并行化这样的do...while循环,我们应该output[id] = calculateFrom(input[id]);提前同时进行一些计算()(不知道结果output[id]是否有用)。如果先前的结果是25,那么结果output[id]就会被拒绝。

  • 也许我们应该考虑 的概率output[id] !== 25。如果概率非常高,我们不会提前进行很多计算,因为它们的结果可能会被拒绝。如果概率绝对低,那么我应该提前做更多的计算。

  • 我们应该听听处理单元的当前状态。如果它已经过度紧张,我们不应该进行不重要的提前计算。但是,如果有足够的资源来处理提前计算,那为什么不呢。- 因为:如果提前计算和之前的计算(这些提前计算所依赖的)被同时处理,那么提前附加也可能减慢之前的计算 - (见我的第二个问题)

我的问题

  1. 并行化这些程序是明智的还是高性能的?
  2. 我应该根据哪些标准来决定处理单元是否有足够的资源来执行我的提前计算任务?或者:我怎么知道我的处理单元是否过度紧张
  3. 您是否知道任何其他并行化此类do...whiles 的计划?你对此有什么想法吗?

我希望我想告诉你的总是很清楚。但如果不是,请评论我的问题。- 感谢您的回答和帮助。

4

1 回答 1

1

如果您使用单个工作组并利用设备的本地内存,则可以轻松地并行完成这项工作。opencl 规范说至少有 16kb 的内存可用于此目的。

一些伪ocl代码:

__kernel doWhileTest(...){

  local int outputBuff[groupSize];
  local int loopBreak[groupsize];

  loopBreak[localId] = 0;
  barrier();

  for(int i = localId;loopBreak[0]==0;i+=groupSize){
    if(i<maxIndex){
      //do interesting calculation on globalInputValues[i]
      //save result to outputBuff[localId]
      //condition failed? set loopBreak[localId] to 1
    }else{
      //set loopBreak condition here as well
    }

    barrier();
    if(localId ==0){
      //loop through all loopBreak values (for j = 0..groupSize)
      //0? copy outputBuff[j] to global memory (globalInputValues[i+j])
      //1? set loopBreak[0] to 1 as well and break this inner loop
    }
    barrier();
  }

  //optional: write some default value to the remaining global buffer, or save a count of the valid outputs

}

上面的 for 循环可能看起来有点奇怪,因为索引是在循环内与 maxIndex 进行比较,而不是在 for 语句中。这是因为所有工作项都需要到达循环内的两个障碍,如果某些工作项提前爆发(即 maxIndex 不是 groupSize 的倍数),这是不可能的。相关问题回复:障碍

另外,我在这里避免全局原子写入,因为它们的性能通常不如本地共享数据/暂存器内存,特别是因为您一次读取/写入整个数据范围。

使用上述设计,可以预先计算一些值,而不会过度处理。在使循环并行的同时,您最多只能丢弃 groupSize 结果。您也可以同时启动许多这样的工作组,但它们会根据自己的数据(而不是全局值或中断条件)各自爆发(或不爆发)。在这种情况下,也许将 groupSize 保持在较低的水平,这样不会破坏的组就不会花费太多时间来处理。(也不要尝试使用全局中断值。我已经自旋锁定了我的 gpu 尝试这个,并得到一个白屏死机。)

于 2012-09-05T11:21:25.487 回答