背景: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个解决方案,因为用厨房水槽击中了代码。
-
切换到g++。这并不难,事实证明,只要在所有-allocs之前加上一个(type *),大多数代码都可以交换。然后输入:
v16s8 condStor = test ? a : b;
-
更棒的是,我发现您可以使用& & &和|的各种组合进行bitbash,就像每个人对整数内部的位所做的一样。技巧在于向量将所有的真值设置为11111111…(-1 unsigned),当使用位运算符时使值保持不变。
- 更棒的是,"键入双关语101"带有一个固有函数:
v16s8 condStor = b; __builtin_ia32_maskmovdqu (a, test, (char *)(&condStor));
这利用了专门用于一次性完成#2所做的工作的函数。
不相信吗?检查程序集:
-
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)
-
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)
-
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++抽象的代价。也许这就是莱纳斯以前一直抱怨的。(不,可能不会。)无论如何,希望这能帮助到一些人!