Opencl 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

尝试这种.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 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