GCC C向量扩展:如何测试比较的结果(用于条件赋值等)



背景:GCC C的内置向量扩展允许将SIMD向量相当自然地表示为C"类型"。根据文档,支持许多内置操作(+,-等)。然而,由于某种原因,三元操作符以及逻辑操作符(&&, ||)只能在c++中使用。这是一个all=C代码库的问题。

问题:在GCC C中,如何实现simd兼容的[无分支]条件:

    v4si a = {2,-1,3,4}, b, indicesLessThan0;
    indicesLessThan0 = a < 0;
    b = indicesLessThan0 ? a : 0;

,更一般地说,如何基于相同的结果执行任意独立的语句块:

v4si c = {9,8,7,6}, d;
for (int i = 0; i < 4; i++) {
  if (indicesLessThan0[i]) { // consider tests one by one
     b[i] = a[i] // as the ternary operator does above
     d[i] = c[i] + 1; // some other independent operation
  }
  else {
     b[i] = 0; // as the ternary operator does above
     d[i] = c[i] - 1; // another independent operation
  } 
}

如果执行语句块比较困难(SIMD分支很糟糕),那么对于任何额外的语句再次执行三元测试是可以的,但(假定)要付出一些效率的代价:

d = indicesLessThan0 ? c + 1 : c - 1; // the other operation in the loop

但是由于某些手册没有解释的原因,三元操作符在C中不起作用。有其他简单的方法吗?如何使用if语句?

我找到了3个解决方案,因为用厨房水槽击中了代码。

  1. 切换到g++。这并不难,事实证明,只要在所有-allocs之前加上一个(type *),大多数代码都可以交换。然后输入:

    v16s8 condStor = test ? a : b;

  2. 更棒的是,我发现您可以使用& & &和|的各种组合进行bitbash,就像每个人对整数内部的位所做的一样。技巧在于向量将所有的真值设置为11111111…(-1 unsigned),当使用位运算符时使值保持不变。

  3. 更棒的是,"键入双关语101"带有一个固有函数:
    v16s8 condStor = b; __builtin_ia32_maskmovdqu (a, test, (char *)(&condStor));
    这利用了专门用于一次性完成#2所做的工作的函数。

不相信吗?检查程序集:

  1. pxor    %xmm1, %xmm1
    movdqa  -64(%rbp), %xmm0
    pcmpeqb %xmm1, %xmm0
    pcmpeqd %xmm1, %xmm1
    pandn   %xmm1, %xmm0
    pxor    %xmm1, %xmm1
    pcmpgtb %xmm0, %xmm1
    movdqa  %xmm1, %xmm0
    movdqa  -32(%rbp), %xmm2
    movdqa  -16(%rbp), %xmm1
    pand    %xmm0, %xmm1
    pandn   %xmm2, %xmm0
    por %xmm1, %xmm0
    movaps  %xmm0, -80(%rbp)
    
  2. movdqa  -64(%rbp), %xmm0
    movdqa  %xmm0, %xmm1
    pand    -16(%rbp), %xmm1
    pcmpeqd %xmm0, %xmm0
    pxor    -64(%rbp), %xmm0
    pand    -32(%rbp), %xmm0
    por %xmm1, %xmm0
    movaps  %xmm0, -80(%rbp)
    
  3. movdqa  -32(%rbp), %xmm0
    movaps  %xmm0, -80(%rbp)
    leaq    -80(%rbp), %rax
    movdqa  -16(%rbp), %xmm0
    movdqa  -64(%rbp), %xmm1
    movq    %rax, %rdi
    maskmovdqu  %xmm1, %xmm0
    

    根据1、2、3的复杂程度判断,我现在看到了c++抽象的代价。也许这就是莱纳斯以前一直抱怨的。(不,可能不会。)无论如何,希望这能帮助到一些人!

最新更新