Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
OpenCL布尔表达式不需要延迟计算_C_Opencl_Lazy Evaluation_Boolean Expression - Fatal编程技术网

OpenCL布尔表达式不需要延迟计算

OpenCL布尔表达式不需要延迟计算,c,opencl,lazy-evaluation,boolean-expression,C,Opencl,Lazy Evaluation,Boolean Expression,来自OpenCL 2.0规范第6.3章“操作员”,第29页: g。逻辑运算符and(&&)或(| |)操作所有标量和向量内置类型。对于 仅标量内置类型,和(&&&&)将仅在左侧操作数 操作数比较不等于0。仅对于标量内置类型,或(| |)将仅计算 如果左侧操作数比较等于0,则为右侧操作数。对于内置向量类型, 这两个操作数都将求值,并且运算符将按组件应用。如果一个操作数为 一个是标量,另一个是矢量,标量可以进行通常的算术转换 指向向量操作数使用的元素类型。然后将标量类型扩展为向量 与向量操作数具有相

来自OpenCL 2.0规范第6.3章“操作员”,第29页:

g。逻辑运算符and(&&)或(| |)操作所有标量和向量内置类型。对于 仅标量内置类型,和(&&&&)将仅在左侧操作数 操作数比较不等于0。仅对于标量内置类型,或(| |)将仅计算 如果左侧操作数比较等于0,则为右侧操作数。对于内置向量类型, 这两个操作数都将求值,并且运算符将按组件应用。如果一个操作数为 一个是标量,另一个是矢量,标量可以进行通常的算术转换 指向向量操作数使用的元素类型。然后将标量类型扩展为向量 与向量操作数具有相同分量数的。手术完成了 组件方面的结果是相同大小的向量

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

int min_非零(int a,int b)
{
返回(a
这可以部分固定,如下所示:

int min_非零(int a,int b)
{
返回select(b,a,a
内置函数可能使用算术实现,以避免分支(例如线性插值)。但是在
和&
中仍然有分支。一个可能更好的方法:

int min_非零(int a,int b)
{
返回select(b,a,(int)(a
但这很快就变得不可读了

因此,我的问题是:是否有更好的方法说服OpenCL编译器放弃布尔表达式的惰性计算(不是全局计算,而是在选定的情况下)


下面是我在这个问题上的实际实验,不再是一个问题。在某些情况下,仍然需要延迟计算,例如:

if(i
因此,优化器不太可能完全禁用它,或者至少在所有适用的情况下禁用它

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

if(p[i]==n\u end&&i)
回来
编译为单个分支:

    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_增量=1;
对于(++i;i
汇编至:

    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;
mov.u32%r29,1;
BB2_4:
mov.u32%r6,%r28;
添加32%r8,%r6,1;
ld.param.u32%r24,[Fill_ColsTailFlags_v0_const_param_0];
setp.ge.u32%p5、%r8、%r24;
@%p5 bra BB2_6;//分支如果i

它似乎对数组访问感到羞涩,因为它不知道如何根据左边的条件分析数组访问的正确性。在这种情况下,将条件切换为
p[i+1]==n\u end&&i
的顺序将去掉分支。将索引更改为常量
i
(其中
j=get\u global\u id(0)
在开头初始化)并不能消除分支。

第三个版本(以及第二个版本)中的类型转换和括号是不必要的,因此您对可读性的反对有些不合理(我理解其意图,但您基本上要求的是一个行为类似于
&
的运算符,唯一的区别是,如果没有大量的括号,您不难理解,我怀疑这一点)。请注意,
&
运算符的非直观优先级正是源自该用法(在C的早期发明
&&
之前)。出于好奇,在什么样的情况下这些“分支”是性能问题?@ParkYoung-Bae阅读了CUDA执行模型。在那里,线程以32个(称为warp)的包执行,所有线程都必须执行相同的指令。如果它们不执行,那么所有32个线程都需要执行两个分支,并启用适当的屏蔽位,这会使它慢2倍。@ParkYoung Bae和我相信我礼貌地解释了问题所在。我不认为这是过早的,因为我正在优化一些代码,以便在GPU上快速运行,我正在尝试为了使我的指令适应内存延迟,所以每个时钟周期都很重要。我不认为有人真的会费心去编写一开始没有针对性能进行优化的OpenCL代码,那么它就可以留在CPU上了(可能除了令人尴尬的并行算法之外,在这些算法中,不需要任何努力就可以获得合理的加速)。