OpenCL布尔表达式不需要的延迟评估

时间:2014-11-18 15:25:16

标签: c opencl lazy-evaluation boolean-expression

从OpenCL 2.0规范,“6.3运算符”一章,第29页:

  

克。逻辑运算符和(&&),或(||)对所有标量和向量内置类型进行操作。对于   仅标量内置类型,和(&&)只会评估左手的右手操作数   操作数比较不等于0 。仅对于标量内置类型,或(||)将仅评估   右手操作数,如果左手操作数等于0 。对于内置矢量类型,   评估两个操作数,并按组件方式应用运算符。如果一个操作数是   标量和另一个是矢量,标量可能会受到通常的算术转换   到向量操作数使用的元素类型。然后将标量类型扩展为向量   与向量操作数具有相同数量的组件。操作完成   分量导致相同大小的矢量。

这意味着使用带有逻辑运算符的表达式将导致分支和线程分歧,从而导致某些并行平台上的性能损失。例如:

int min_nonzero(int a, int b)
{
    return (a < b && a != 0)? a : b; // branch
}

这可以部分修复,如:

int min_nonzero(int a, int b)
{
    return select(b, a, a < b && a != 0); // branch because of &&
}

可能使用算术来实现内置函数select以避免分支(例如,作为线性插值)。但&&中仍有分支。一种可能更好的方法:

int min_nonzero(int a, int b)
{
    return select(b, a, (int)(a < b) & (int)(a != 0)); // branch free
}

但很快变得难以理解。

所以我的问题是:是否有更好的方法来说服OpenCL编译器放弃对布尔表达式的懒惰评估(不是全局但在特定情况下)


以下是我在这件事上的实际实验,不再是一个问题了。在某些情况下仍然需要延迟评估,例如:

if(i < N && array[i] == x) // will go OOB without lazy evaluation

因此,优化程序不太可能完全禁用它,或者至少在所有适用的情况下都禁用它。

我正在查看由NVIDIA 320.49驱动程序生成的一些PTX,它只会优化右侧无数组访问的情况:

if(p[i] == n_end && i)
    return;

编译成一个分支:

    setp.ne.s32     %p2, %r17, %r5; // p[i] != n_end
    setp.eq.s32     %p3, %r28, 0; // !i
    or.pred     %p4, %p2, %p3; // (p[i] != n_end || !i) = !(p[i] == n_end && i)
    @%p4 bra    BB2_3; // branch
    ret;

BB2_3:

然而这:

int n_increment = 1;
for(++ i; i < n_cols_B && p[i + 1] == n_end; ++ i)
    ++ n_increment;

汇编为:

    mov.u32     %r29, 1;

BB2_4:
    mov.u32     %r6, %r28;
    add.s32     %r8, %r6, 1;
    ld.param.u32    %r24, [Fill_ColsTailFlags_v0_const_param_0];
    setp.ge.u32     %p5, %r8, %r24;
    @%p5 bra    BB2_6; // branch if i < n_cols_B

    shl.b32     %r19, %r6, 2;
    ld.param.u32    %r25, [Fill_ColsTailFlags_v0_const_param_1];
    add.s32     %r20, %r19, %r25;
    ld.const.u32    %r21, [%r20+8];
    setp.eq.s32     %p6, %r21, %r5;
    @%p6 bra    BB2_7; // branch if p[i + 1] == n_end

BB2_6:
    shl.b32     %r22, %r5, 2;
    ld.param.u32    %r27, [Fill_ColsTailFlags_v0_const_param_2];
    add.s32     %r23, %r27, %r22;
    st.global.u32   [%r23], %r29;
    ret;

BB2_7:
    add.s32     %r29, %r29, 1;
    mov.u32     %r28, %r8;
    bra.uni     BB2_4;

似乎它对数组访问感到害羞,因为它不知道如何根据左边的条件分析数组访问的正确性。在这种情况下,条件的切换顺序为p[i + 1] == n_end && i < n_cols_B摆脱了分支。将索引更改为常量i < n_cols_B && B_p[j] == n_end(其中j = get_global_id(0)在开始时初始化)不会删除分支。

0 个答案:

没有答案