使用OpenCL时并行化...

时间:2012-09-04 21:02:52

标签: parallel-processing cpu opencl gpu aparapi

原则

我知道,这样一个简单的计算是不值得精心平行的。这是一个例子,数学运算只是一个占位符,可用于一些更有趣的计算。

[伪代码]

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]2的平方1}}或9不会被用作output的项目(因为output[id] !== 25 true id = 1 input[id] = 5input[id] )。

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

广义,我说的是计算结果 output[id] = calculateFrom(input[id]);id)可能与每个output[id]无关的情况 - 结果的需要(do...while)取决于另一个计算的结果。

我的目标

我希望使用 OpenCL 内核和队列尽可能以并行和高性能执行此循环。

我的想法

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

  • 也许我们应该考虑do...while的概率。 如果概率非常高,我们将不会进行很多计算 时间因为他们的结果可能会被拒绝。如果 概率绝对低,那么我应该做更多的计算 提前。

  • 我们应该听取处理单元的当前状态。如果 它已经过度训练了,我们不应该提前做不重要的事情 计算。但是如果有足够的资源来处理 提前计算,为什么不呢。 - 因为:如果提前计算和之前的计算(这些计算依赖于这些计算)在同一时间进行处理,那么提前的额外计算也可能会减慢先前的计算速度 - (参见我的第二个问题)

我的问题

  1. 并行化这些程序是明智的还是高性能的?
  2. 根据哪个标准,我应该决定处理单元是否有足够的资源来进行提前计算?或者:我如何知道我的处理单元是否过于训练
  3. 您是否知道有关此类{{1}}并行化的其他计划?你对此有什么想法吗?
  4. 我希望我总能清楚地告诉你什么。但如果不是,请评论我的问题。 - 感谢您的回答和帮助。

1 个答案:

答案 0 :(得分:1)

如果您使用单个工作组并利用设备的本地内存,则可以轻松地并行完成此项工作。 opencl spec表示至少有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的倍数),这是不可能的。 Related question re: barriers

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

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