我的内核中有一个for循环,我已经硬编码迭代我的代码的固定数量的循环:
for (int kk = 0; kk < 50000; kk++)
{
<... my code here ...>
}
我不认为循环中的代码与我的问题相关,它是一些非常简单的表查找和整数数学。
我想让我的内核代码更灵活,所以我修改了循环,以便我的循环(50000)的迭代次数被内核输入参数'num_loops'替换。
for (int kk = 0; kk < num_loops; kk++)
{
<... more code here ...>
}
我发现的是即使我的宿主程序用
调用内核num_loops = 50000
与之前的硬编码值相同,我内核的性能几乎减少了一半。
我正在试图找出导致性能下降的原因。我想这与OpenCL编译器无法有效地展开循环有关?
有没有办法在不招致性能损失的情况下做我想做的事情?
更新:以下是使用“#pragma unroll”播放的一些结果
不幸的是,展开循环似乎无法解决我的性能问题。
即使展开硬编码循环也会降低性能。
这是具有硬编码值的正常循环(最佳性能):
for (int kk = 0; kk < 50000; kk++)
// Time to execute = 0.18 (40180 Mi ops/sec)
如果我展开循环,事情会变得更糟:
#pragma unroll
// or #pragma unroll 50000
for (int kk = 0; kk < 50000; kk++)
// Time to execute = 0.22 (33000 Mi ops/sec)
这是使用变量num_loops = 50000的循环:
for (int kk = 0; kk < num_loops; kk++)
// Time to execute = 0.26 (27760 Mi ops/sec)
#pragma unroll 50000
for (int kk = 0; kk < num_loops; kk++)
// Time to execute = 0.26 (27760 Mi ops/sec)
#pragma unroll
for (int kk = 0; kk < num_loops; kk++)
// Time to execute = 0.24 (30280 Mi ops/sec)
使用带有直接“#pragma unroll”的num_loops变量时情况会好一些,但即使这个性能仍然比硬编码的展开版本慢约25%。
关于如何使用num_loops作为循环变量而不会导致性能下降的任何其他想法?
答案 0 :(得分:9)
是的,性能下降的最可能原因是编译器无法展开循环。您可以尝试改善这种情况。
您可以将参数定义为通过程序构建选项传递的预处理器宏。这是用于构建仅在运行时作为编译时常量在内核中已知的值的常用技巧。例如:
clBuildProgram(program, 1, &device, "-Dnum_loops=50000", NULL, NULL);
您可以使用sprintf
动态构建构建选项,以使其更加灵活。显然,如果您不需要经常更改参数,这将是值得的,因此重新编译的开销不会成为问题。
您可以调查您的OpenCL平台是否使用任何编译指示,这些编译指示可以为编译器提供有关循环展开的提示。例如,一些OpenCL编译器识别#pragma unroll
(或类似)。 OpenCL 2.0具有以下属性:__attribute__((opencl_unroll_hint))
。
您可以手动展开循环。这看起来如何取决于您对num_loops
参数的假设。例如,如果您知道(或可以确保)它将始终是4的倍数,您可以执行以下操作:
for (int kk = 0; kk < num_loops;)
{
<... more code here ...>
kk++;
<... more code here ...>
kk++;
<... more code here ...>
kk++;
<... more code here ...>
kk++;
}
即使您不能做出这样的假设,您仍然可以执行手动展开,但可能需要一些额外的工作(例如,完成任何剩余的迭代)。
答案 1 :(得分:0)
for循环重复计算(;;)中的第二个语句,以确定是否继续循环。这样的条件语句总是导致控制流分叉并丢弃不需要的计算,这是浪费的。
正确的方法是在内核中添加另一个维度,并将该维度完全放在一个工作组中,以便在一个计算单元内顺序执行。