我知道,这样一个简单的计算是不值得精心平行的。这是一个例子,数学运算只是一个占位符,可用于一些更有趣的计算。
[伪代码]
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] = 5
和input[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
的概率。
如果概率非常高,我们将不会进行很多计算
时间因为他们的结果可能会被拒绝。如果
概率绝对低,那么我应该做更多的计算
提前。
我们应该听取处理单元的当前状态。如果 它已经过度训练了,我们不应该提前做不重要的事情 计算。但是如果有足够的资源来处理 提前计算,为什么不呢。 - 因为:如果提前计算和之前的计算(这些计算依赖于这些计算)在同一时间进行处理,那么提前的额外计算也可能会减慢先前的计算速度 - (参见我的第二个问题)
我希望我总能清楚地告诉你什么。但如果不是,请评论我的问题。 - 感谢您的回答和帮助。
答案 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尝试这个,并获得了死亡的白屏。)