如何将一系列if / else if / else if / chain转换为线性循环代码

时间:2014-09-25 20:50:38

标签: c++ vectorization boolean-logic boolean-expression boolean-operations

我有一个算法的核心,我想从基本上一系列的if / else if / else if / else i / chain转换为大约20深度到一个可以线性方式完成的循环。条件是简单的,具有4种可能性之一(A [i]< B [j]),(A [i]< = B [j]),(A [i]> B [j]), (A [i]> = B [j])。如何将所有这些转换为单个条件。例如,链可能是这样的。

if (A[i+0] <  B[j+0]) break
if (A[i+1] <= B[j+1]) break
if (A[i+2] >  B[j+2]) break
if (A[i+3] >= B[j+3]) break
if (A[i+4] >= B[j+4]) break
....

每个条件可能是4个中的1个,但我想将它们全部转换为一组没有大小写的步骤,以便它可以在循环中完成(或者可能与向量内在函数并行)

// Given a list R[n] of 4 possible relations loop over all the data
int result = 1;
for (i = 0; i < num_relations && result; ++i) {           
       // How do I convert this to linear code which does the equivalent of
       // (the value of R[n] and what relation it maps is flexible, this is an example)
       case (R[n]) {
          0 : result = A[i] <  B[i]; break;
          1 : result = A[i] <= B[i]; break;
          2 : result = A[i] >  B[i]; break;
          3 : result = A[i] >= B[i]; break;
       }
}

可以使用的(无符号数字)的一些属性是

(A > B) ^ 1 === (A <= B) ^ 0

可以将上述内容优化为优于

的内容
result = 1;
for (i = 0; i < num_relations && result; ++i) {           
   result = ((A[i] <  B[i]) && (R[i] == 0)) ||
            ((A[i] <= B[i]) && (R[i] == 1)) ||
            ((A[i] >  B[i]) && (R[i] == 2)) ||
            ((A[i] >= B[i]) && (R[i] == 3));
}

3 个答案:

答案 0 :(得分:1)

如果没有矢量化,您的if()序列就会尽可能快。在这种情况下,每个条件必须有一个比较指令,你不能绕过它(即使有些机器可以优化分支除外)。

使用矢量化,您可以并行执行多个比较,但要求它们都在同一方向。但这可以通过转换输入值来实现:

int signs[] = {1, 1, -1, -1, -1, ...};
int equals[] = {0, 1, 0, 1, 1, ...};
if (A[i+0] <  signs[0]*B[j+0] + equals[0]) break;
if (A[i+1] <  signs[1]*B[j+1] + equals[1]) break;
if (A[i+2] <  signs[2]*B[j+2] + equals[2]) break;
if (A[i+3] <  signs[3]*B[j+3] + equals[3]) break;
if (A[i+4] <  signs[4]*B[j+4] + equals[4]) break;
...

但是,此代码的向量化应该会失败,因为在评估第一个条件并显示不满足之前,编译器不需要从内存加载A[i+1]。因此,您需要使条件评估彼此不相关:

int signs[] = {1, 1, -1, -1, -1, ...};
int equals[] = {0, 1, 0, 1, 1, ...};
int doBreak = 0;
doBreak |= (A[i+0] <  signs[0]*B[j+0] + equals[0]);
doBreak |= (A[i+1] <  signs[1]*B[j+1] + equals[1]);
doBreak |= (A[i+2] <  signs[2]*B[j+2] + equals[2]);
doBreak |= (A[i+3] <  signs[3]*B[j+3] + equals[3]);
doBreak |= (A[i+4] <  signs[4]*B[j+4] + equals[4]);
...
if(doBreak) break;

现在你可以自由地制作一个循环。

答案 1 :(得分:0)

  1. 创建符合签名的四个辅助函数:

    bool (*)(int A, int B);
    
    bool isLess(int A, int B) { return A < B; }
    bool isLessOrEqual(int A, int B) { return A <= B; }
    bool isGreater(int A, int B) { return A > B; }
    bool isGreaterOrEqual(int A, int B) { return A >= B; }
    
  2. 将它们放入数组中。

    typedef bool (*CompareFunction)(int A, int B);
    CompareFunction functions[] = {isLess, isLessOrEqual, isGreater, isGreaterOrEqual};
    
  3. 使用循环中的函数数组。

    for (i = 0; i < num_relations && result; ++i) {           
      result = functions[i](A[i], B[i]);
    }
    

答案 2 :(得分:0)

您可以使用按位AND

result = 1;
for (i = 0; i < num_relations && result; ++i) {           
    delta = A[i] - B[i];

    //  R[i] & 1 is true for 1 and 3
    //  R[i] & 2 is true for 2 and 3   
    if (delta == 0) {
        result = (R[i] & 1);
    }
    else  {
        //  exclusive or
        result = (delta < 0) ^ (R[i] & 2);
    }
}

诀窍是结合案例,从而减少关系操作的数量。