它并没有真正“卡住”。它只是陷入了优化内核的地狱尝试中。主要是通过展开具有固定大小的循环(顺便说一句,通过发现根本没有使用 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;
}
你可以看到它实际上并没有计算任何东西 - 编译器已经很难发现实际上没有什么可做的。
将此与添加行时生成的输出进行比较
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;
}
显然,它已经展开了一些循环,现在他无法再优化它们了。我假设当循环“e”也被激活时,这种展开(或优化未使用的循环)所需的时间至少呈二次方增加。所以如果你给他几个小时,他实际上可能也会完成编译......
正如 Tom Fenech 在 https://stackoverflow.com/a/22011454 中已经说过的那样,可以通过将 -cl-opt-disable 传递给 clBuildProgram 来缓解这个问题。
或者,您可以选择性地关闭每个循环的展开优化:当您插入时
#pragma unroll 1
直接在 for 循环之前,您实际上禁用了此特定循环的展开。
重要不要盲目地插入带有任意值的unroll pragma。使用1 是安全的,但对于其他值,您必须手动确保它不会影响程序的正确性。请参阅 CUDA 编程指南,“B.21.#pragma unroll”部分。
在这种情况下,在最里面的两个循环(d 和 e)之前插入 #pragma unroll 1 似乎就足够了,以便进行足够的优化以快速构建程序。
编辑:sigh 修剪快了 4 分钟... :-(