Opencl clBuildProgram陷入嵌套循环
尝试这种.cl文件时,clBuildProgram似乎没有出现任何错误消息:Opencl clBuildProgram陷入嵌套循环,opencl,Opencl,尝试这种.cl文件时,clBuildProgram似乎没有出现任何错误消息: __local int bar(int a, int b, int c, int d, int e) { return a*b*c*d; // 'e' not used } __kernel void foobar(__global int * notusedvariable) { int foo=1; for (int a=1; a<=10; a++) for (int
__local int bar(int a, int b, int c, int d, int e)
{
return a*b*c*d; // 'e' not used
}
__kernel void foobar(__global int * notusedvariable)
{
int foo=1;
for (int a=1; a<=10; a++)
for (int b=1; b<=10; b++)
for (int c=1; c<=10; c++)
for (int d=1; d<=10; d++)
for (int e=1; e<=10; e++)
foo *= bar(a,b,c,d,e);
}
GPU是Geforce 210,这是我能找到的最便宜的一个。您正在进行的计算将得到一个非常大的数字 我在我的硬件(NVIDIA GTX480)上遇到与您相同的问题,但我不认为这与硬件有关。您只是生成一个编译时已知的数字,该数字太大,无法对
int
变量类型进行预计算。我将int
更改为long
,程序现在开始构建
编辑
我刚刚用英特尔平台试过这个。它编译得很好。您还可以通过将开关
-cl opt disable
传递到clBuildProgram
使其在NVIDIA上工作。这将禁用所有优化-您可能会幸运地使用其他一些编译器开关。有关详细信息,请参阅。Nvidia OpenCL编译器可能正在执行循环展开。如果对每个嵌套循环都这样做,将生成大量代码
有一个特定于c Nvidia的OpenCL扩展
可以用来更好地控制循环展开。到
引用其文件:
用户可以指定展开源程序中的循环。这
通过pragma完成。这个pragma的语法如下
#pragma展开[展开因子]
pragma展开可以选择指定展开因子。布拉格马
必须放置在循环的正前方,且仅适用于该循环
循环
如果未指定展开因子,则编译器将尝试执行以下操作
循环的完全展开或完全展开。如果循环展开因子为
指定编译器将执行部分循环展开。环路
因子(如果指定)必须是编译时非负整数
不变的
循环展开因子为1表示编译器不应展开
循环
完整的展开规格在下列情况下无效:
循环不是编译时可计算的
默认情况下,它听起来像是将展开某些较低的最大限制(例如,在您的示例中为10),但如果它们是嵌套的,则可能仍然会展开(展开检查逻辑可能不够复杂,无法检查嵌套循环)
您可以尝试#pragma unroll 1
禁用展开:
int foo=1;
#pragma unroll 1
for (int a=1; a<=10; a++)
#pragma unroll 1
for (int b=1; b<=10; b++)
#pragma unroll 1
for (int c=1; c<=10; c++)
#pragma unroll 1
for (int d=1; d<=10; d++)
#pragma unroll 1
for (int e=1; e<=10; e++)
foo *= bar(a,b,c,d,e);
在OpenCL源代码文件的顶部也是如此。它并不是真的“卡住了”。它只是陷入了一个地狱般的尝试,以优化内核。主要通过以固定大小展开循环(顺便说一句,通过发现根本没有使用foo
变量!)
例如,当循环a…d被启用(并且e被关闭)时,为内核创建的二进制文件如下所示:
.entry foobar(
.param .u32 .ptr .global .align 4 foobar_param_0
)
{
.reg .pred %p<4>;
.reg .s32 %r<13>;
mov.u32 %r10, 0;
BB0_1:
add.s32 %r10, %r10, 1;
mov.u32 %r11, 0;
BB0_2:
mov.u32 %r12, 10;
BB0_3:
add.s32 %r12, %r12, -2;
setp.ne.s32 %p1, %r12, 0;
@%p1 bra BB0_3;
add.s32 %r11, %r11, 1;
setp.ne.s32 %p2, %r11, 10;
@%p2 bra BB0_2;
setp.ne.s32 %p3, %r10, 10;
@%p3 bra BB0_1;
ret;
}
作为内核的最后一行:现在,计算不能被跳过和优化。经过一段时间的编译,它产生了结果
.entry foobar(
.param .u32 .ptr .global .align 4 foobar_param_0
)
{
.reg .pred %p<4>;
.reg .s32 %r<80>;
mov.u32 %r79, 1;
mov.u32 %r73, 0;
mov.u32 %r72, %r73;
BB0_1:
add.s32 %r7, %r73, 1;
add.s32 %r72, %r72, 2;
mov.u32 %r76, 0;
mov.u32 %r74, %r76;
mov.u32 %r73, %r7;
mov.u32 %r75, %r7;
BB0_2:
mov.u32 %r9, %r75;
add.s32 %r74, %r74, %r72;
mov.u32 %r78, 10;
mov.u32 %r77, 0;
BB0_3:
add.s32 %r40, %r9, %r77;
mul.lo.s32 %r41, %r40, %r79;
mul.lo.s32 %r42, %r40, %r41;
add.s32 %r43, %r74, %r77;
mul.lo.s32 %r53, %r42, %r40;
mul.lo.s32 %r54, %r53, %r40;
mul.lo.s32 %r55, %r54, %r40;
mul.lo.s32 %r56, %r55, %r40;
mul.lo.s32 %r57, %r56, %r40;
mul.lo.s32 %r58, %r57, %r40;
mul.lo.s32 %r59, %r58, %r40;
mul.lo.s32 %r60, %r59, %r40;
mul.lo.s32 %r61, %r60, %r43;
mul.lo.s32 %r62, %r61, %r43;
mul.lo.s32 %r63, %r62, %r43;
mul.lo.s32 %r64, %r63, %r43;
mul.lo.s32 %r65, %r64, %r43;
mul.lo.s32 %r66, %r65, %r43;
mul.lo.s32 %r67, %r66, %r43;
mul.lo.s32 %r68, %r67, %r43;
mul.lo.s32 %r69, %r68, %r43;
mul.lo.s32 %r70, %r69, %r43;
mul.lo.s32 %r79, %r70, -180289536;
add.s32 %r77, %r77, %r74;
add.s32 %r78, %r78, -2;
setp.ne.s32 %p1, %r78, 0;
@%p1 bra BB0_3;
add.s32 %r76, %r76, 1;
add.s32 %r30, %r9, %r7;
setp.ne.s32 %p2, %r76, 10;
mov.u32 %r75, %r30;
@%p2 bra BB0_2;
setp.ne.s32 %p3, %r7, 10;
@%p3 bra BB0_1;
ld.param.u32 %r71, [foobar_param_0];
st.global.u32 [%r71], %r79;
ret;
}
就在for循环之前,您实际上禁用了此特定循环的展开
重要不要盲目插入带有任意值的展开
杂注。使用1
是安全的,但是对于其他值,您必须手动确保它不会影响程序的正确性。请参阅《CUDA编程指南》中的“B.21.#pragma展开”一节
在这种情况下,在两个最里面的循环(d和e)之前插入#pragma unroll 1
似乎就足够了,以便能够进行足够的优化以快速构建程序
编辑:叹息普伦奇快了4分钟…:-(但无符号int也会发生这种情况。它们应该定义溢出。(实际上,我想搜索函数的零。通过首先尝试溢出算法,我可以找到使用较长int检查的候选项。)@JoriMäntysalo我已经用更多的细节更新了我的答案。谢谢,我会将此标记为已接受。我想我也可以将“10”作为参数传递给内核;这样编译器就不能(过度)优化代码了。
.entry foobar(
.param .u32 .ptr .global .align 4 foobar_param_0
)
{
.reg .pred %p<4>;
.reg .s32 %r<13>;
mov.u32 %r10, 0;
BB0_1:
add.s32 %r10, %r10, 1;
mov.u32 %r11, 0;
BB0_2:
mov.u32 %r12, 10;
BB0_3:
add.s32 %r12, %r12, -2;
setp.ne.s32 %p1, %r12, 0;
@%p1 bra BB0_3;
add.s32 %r11, %r11, 1;
setp.ne.s32 %p2, %r11, 10;
@%p2 bra BB0_2;
setp.ne.s32 %p3, %r10, 10;
@%p3 bra BB0_1;
ret;
}
notusedvariable[0]=foo;
.entry foobar(
.param .u32 .ptr .global .align 4 foobar_param_0
)
{
.reg .pred %p<4>;
.reg .s32 %r<80>;
mov.u32 %r79, 1;
mov.u32 %r73, 0;
mov.u32 %r72, %r73;
BB0_1:
add.s32 %r7, %r73, 1;
add.s32 %r72, %r72, 2;
mov.u32 %r76, 0;
mov.u32 %r74, %r76;
mov.u32 %r73, %r7;
mov.u32 %r75, %r7;
BB0_2:
mov.u32 %r9, %r75;
add.s32 %r74, %r74, %r72;
mov.u32 %r78, 10;
mov.u32 %r77, 0;
BB0_3:
add.s32 %r40, %r9, %r77;
mul.lo.s32 %r41, %r40, %r79;
mul.lo.s32 %r42, %r40, %r41;
add.s32 %r43, %r74, %r77;
mul.lo.s32 %r53, %r42, %r40;
mul.lo.s32 %r54, %r53, %r40;
mul.lo.s32 %r55, %r54, %r40;
mul.lo.s32 %r56, %r55, %r40;
mul.lo.s32 %r57, %r56, %r40;
mul.lo.s32 %r58, %r57, %r40;
mul.lo.s32 %r59, %r58, %r40;
mul.lo.s32 %r60, %r59, %r40;
mul.lo.s32 %r61, %r60, %r43;
mul.lo.s32 %r62, %r61, %r43;
mul.lo.s32 %r63, %r62, %r43;
mul.lo.s32 %r64, %r63, %r43;
mul.lo.s32 %r65, %r64, %r43;
mul.lo.s32 %r66, %r65, %r43;
mul.lo.s32 %r67, %r66, %r43;
mul.lo.s32 %r68, %r67, %r43;
mul.lo.s32 %r69, %r68, %r43;
mul.lo.s32 %r70, %r69, %r43;
mul.lo.s32 %r79, %r70, -180289536;
add.s32 %r77, %r77, %r74;
add.s32 %r78, %r78, -2;
setp.ne.s32 %p1, %r78, 0;
@%p1 bra BB0_3;
add.s32 %r76, %r76, 1;
add.s32 %r30, %r9, %r7;
setp.ne.s32 %p2, %r76, 10;
mov.u32 %r75, %r30;
@%p2 bra BB0_2;
setp.ne.s32 %p3, %r7, 10;
@%p3 bra BB0_1;
ld.param.u32 %r71, [foobar_param_0];
st.global.u32 [%r71], %r79;
ret;
}
#pragma unroll 1