Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/56.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
C++ 任何人都可以修改以下代码以避免使用“if”吗?_C++_C_Logic - Fatal编程技术网

C++ 任何人都可以修改以下代码以避免使用“if”吗?

C++ 任何人都可以修改以下代码以避免使用“if”吗?,c++,c,logic,C++,C,Logic,我目前正在cuda上工作,我被下面的代码卡住了。该代码最初是用matlab编写的,我正在尝试使用cuda重新编写: Pv = 0; Nv =0; [LOOP] v1 = l(li); v2 = l(li+1); if((v1>0) && (v2>0)) Pv = Pv + 1; elseif((v1<0) && (v2<0)) Nv = Nv +1; elseif((v1>0) && (v2<0)

我目前正在cuda上工作,我被下面的代码卡住了。该代码最初是用matlab编写的,我正在尝试使用cuda重新编写:

Pv = 0; Nv =0;
[LOOP]
v1 = l(li);
v2 = l(li+1);
if((v1>0) && (v2>0))
    Pv = Pv + 1;
elseif((v1<0) && (v2<0))
    Nv = Nv +1;
elseif((v1>0) && (v2<0))
    r = v1/(v1-v2);
    Pv = Pv + r;
    Nv = Nv + 1 - r;
elseif((v1<0) && (v2>0))
    r = v2/(v2-v1);
    Pv = Pv + r;
    Nv = Nv + 1 - r;
end
[LOOP END]
但是,在cuda体系结构中,如果表达式有时很昂贵,我相信有一些方法可以避免使用它,尽管我现在无法理解

代码的主要目的是计算正区间与负区间的比值,并分别相加。在大多数情况下,v1和v2有相同的符号,但一旦它们有不同的符号,我就必须使用一堆if,甚至abs来处理这种情况


那么,有谁能帮我用C重新编写代码,同时尽可能少地使用?

利用您使用的每个表达式都给出一个0或1二进制表达式的事实,我可以将其归结为一个三元运算符:

// r is 0 if not case 3 or 4
r = (v1==v2)?0:((v1>v2)*v1-(v2>v1)*v2)/(v1-v2) * (v1*v2<0);
Pv += r + // cases 3 & 4
     (v1>0) && (v2>0); // case 1
Nv += r + // cases 3 & 4
     (v1<=0) || (v2<=0); // cases 2, 3 and 4
编辑:进一步优化未经测试


然而,这听起来很像过早的优化。是否真的是if语句的数量会引起问题?

利用您使用的每个表达式都给出一个0或1二进制表达式的事实,我可以将其归结为一个三元运算符:

// r is 0 if not case 3 or 4
r = (v1==v2)?0:((v1>v2)*v1-(v2>v1)*v2)/(v1-v2) * (v1*v2<0);
Pv += r + // cases 3 & 4
     (v1>0) && (v2>0); // case 1
Nv += r + // cases 3 & 4
     (v1<=0) || (v2<=0); // cases 2, 3 and 4
编辑:进一步优化未经测试

然而,这听起来很像过早的优化。是否真的是if语句的数量会导致问题?

添加到:

这个

正如a中abligh所指出的,在ra初始化过程中,我们可能会得到除以零的结果,因此我们只想进行适当的计算:

double r0(double v1, double v2)
{
  return 0;
}

double r1(double v1, double v2)
{
  return ((v1 > v2) * v1 - (v2 > v1) * v2) / (v1 - v2) * (v1 * v2 < 0);
}

double (*rf[2])(double v1, double v2) = {r0, r1}; 

...

r = rf[fabs(v1 - v2) < EPSILON](v1, v2); /* With EPSILON like 0.00001 or what ever accuracy is needed. */
添加到的:

这个

正如a中abligh所指出的,在ra初始化过程中,我们可能会得到除以零的结果,因此我们只想进行适当的计算:

double r0(double v1, double v2)
{
  return 0;
}

double r1(double v1, double v2)
{
  return ((v1 > v2) * v1 - (v2 > v1) * v2) / (v1 - v2) * (v1 * v2 < 0);
}

double (*rf[2])(double v1, double v2) = {r0, r1}; 

...

r = rf[fabs(v1 - v2) < EPSILON](v1, v2); /* With EPSILON like 0.00001 or what ever accuracy is needed. */

由于您指出,同号非零操作数的情况是最常见的情况,因此最好使用分支处理罕见的情况3和4,特别是因为它们需要昂贵的除法运算,而我们不希望在公共快速路径中执行。考虑到最小值和最大值以及三元运算符是由GPU中的硬件直接支持的,并且GPU对谓词有广泛的支持,我建议进行如下所示的轻微重写。我已经使用从集合{-0.0f,-3.0f,-5.0f,0.0f,3.0f,5.0f}中抽取的两个元素的随机组合来测试这一点,以确保涵盖v1==0或v2==0的情况

float Nv, Pv, v1, v2;
float r;
if ((v1 > 0) && (v2 > 0)) {
    Pv = Pv + 1;
} 
if ((v1 < 0) && (v2 < 0)) {
    Nv = Nv + 1;
}
if (((v1 > 0) && (v2 < 0)) || ((v1 < 0) && (v2 > 0))) { // rare case
    float s = min (v1, v2);
    float t = max (v1, v2);
    r = t / (t - s);
    Pv = Pv + r;
    Nv = Nv + 1 - r;
}

由于您指出,同号非零操作数的情况是最常见的情况,因此最好使用分支处理罕见的情况3和4,特别是因为它们需要昂贵的除法运算,而我们不希望在公共快速路径中执行。考虑到最小值和最大值以及三元运算符是由GPU中的硬件直接支持的,并且GPU对谓词有广泛的支持,我建议进行如下所示的轻微重写。我已经使用从集合{-0.0f,-3.0f,-5.0f,0.0f,3.0f,5.0f}中抽取的两个元素的随机组合来测试这一点,以确保涵盖v1==0或v2==0的情况

float Nv, Pv, v1, v2;
float r;
if ((v1 > 0) && (v2 > 0)) {
    Pv = Pv + 1;
} 
if ((v1 < 0) && (v2 < 0)) {
    Nv = Nv + 1;
}
if (((v1 > 0) && (v2 < 0)) || ((v1 < 0) && (v2 > 0))) { // rare case
    float s = min (v1, v2);
    float t = max (v1, v2);
    r = t / (t - s);
    Pv = Pv + r;
    Nv = Nv + 1 - r;
}

我同意这似乎是过早的优化。这个代码段是否更快取决于数据:它在聚合情况下做了更多的工作。@Jez:的确如此。我只计算了r的一个值,使它稍微不那么糟糕。您是否也尝试过优化源空间-还有剩余的空间可以移除-由于我不敢在你的答案中乱涂乱画,请看看我的答案。我同意这似乎是过早的优化。这个代码段是否更快取决于数据:它在聚合情况下做了更多的工作。@Jez:的确如此。我只计算了r的一个值,使它稍微不那么糟糕。您是否也尝试过优化源空间-还有剩余的空间可以移除-由于我不敢在你的答案中乱涂乱画,请看看我的答案。双ra[1]不应该是双ra[2]吗?这本书的重点是什么!!在ra中[!!v1==v2]?@Jez:Aargh,是的,当然。固定的谢谢。:-}CUDA也不喜欢线程本地数组的动态索引,所以我希望ra放在堆栈帧中,而不是寄存器中。这几乎肯定会比分支对性能的影响更大。@Jez:the!!只是糖。它确保v1==v2的计算结果为真布尔值。在执行硬代码C时,这并不是必须的。但是,第二次看你是对的,这是多余的。用v1-v2*v1*v2=0除法怎么样?这会使除法安全,如果不想被除法,那么输出就没有多大关系,因为它只在v1和v2的符号不同时使用。避免比较浮点值是否相等。double ra[1]不应该是double ra[2]吗?这本书的重点是什么!!在ra中[!!v1==v2]?@Jez:Aargh,是的,当然。固定的谢谢。:-}CUDA也不喜欢线程本地数组的动态索引,所以我希望ra放在堆栈框架中,而不是r
登记员。这几乎肯定会比分支对性能的影响更大。@Jez:the!!只是糖。它确保v1==v2的计算结果为真布尔值。在执行硬代码C时,这并不是必须的。但是,第二次看你是对的,这是多余的。用v1-v2*v1*v2=0除法怎么样?这会使除法安全,如果不想被除法,那么输出就没有多大关系,因为它只在v1和v2的符号不同时使用。避免比较浮点值是否相等。Pv、Nv、v1、v2的类型是什么?浮动,双人?CUDA编译器可能使用if转换来断言代码,或者将这些赋值转换为三元运算符,因此尝试手动将其转换为无分支代码可能是过早的优化。使用cuobjdump-dump SASS检查生成的sas,并分析内核以确定分支分歧是否是一个很大的问题,这将非常有用。@njuffa它们都是浮点变量。我从来没有想过检查二进制代码,如果可能的话我会试试。Pv、Nv、v1、v2的类型是什么?浮动,双人?CUDA编译器可能使用if转换来断言代码,或者将这些赋值转换为三元运算符,因此尝试手动将其转换为无分支代码可能是过早的优化。使用cuobjdump-dump SASS检查生成的sas,并分析内核以确定分支分歧是否是一个很大的问题,这将非常有用。@njuffa它们都是浮点变量。我从来没有想过检查二进制代码,如果可能的话我会试试。
   /*0008*/         FSETP.LT.AND P2, PT, RZ, c[0x0][0x2c], PT;
   /*0010*/         FSETP.GT.AND P3, PT, RZ, c[0x0][0x28], PT;
   /*0018*/         FSETP.LT.AND P1, PT, RZ, c[0x0][0x28], PT;
   /*0020*/         MOV32I R4, 0x3f800000;
   /*0028*/         FSETP.GT.AND P4, PT, RZ, c[0x0][0x2c], PT;
   /*0030*/         PSETP.AND.AND P5, PT, P3, P2, PT;
   /*0038*/         PSETP.AND.AND P0, PT, P1, P2, PT;
   /*0040*/         FADD R0, R4, c[0x0][0x24];
   /*0048*/         PSETP.AND.AND P2, PT, P3, P4, PT;
   /*0050*/         FADD R4, R4, c[0x0][0x20];
   /*0058*/         PSETP.AND.OR P1, PT, P1, P4, P5;
   /*0060*/         MOV R2, c[0x0][0x30];
   /*0068*/         MOV R3, c[0x0][0x34];
   /*0070*/         SEL R0, R0, c[0x0][0x24], P0;
   /*0078*/         SEL R6, R4, c[0x0][0x20], P2;
   /*0080*/    @!P1 BRA 0xc8;
   /*0088*/         MOV R4, c[0x0][0x28];
   /*0090*/         FMNMX R5, R4, c[0x0][0x2c], PT;
   /*0098*/         FMNMX R4, R4, c[0x0][0x2c], !PT;
   /*00a0*/         FADD R5, R4, -R5;
   /*00a8*/         CAL 0xe0;                 // division
   /*00b0*/         FADD R5, R6, 1;
   /*00b8*/         FADD R0, R0, R4;
   /*00c0*/         FADD R6, R5, -R4;
   /*00c8*/         FADD R0, R0, R6;