当使用C的GCC向量扩展时,如何检查向量上的所有值是否为零?
例如:
#include <stdint.h>
typedef uint32_t v8ui __attribute__ ((vector_size (32)));
v8ui*
foo(v8ui *mem) {
v8ui v;
for ( v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 };
v[0] || v[1] || v[2] || v[3] || v[4] || v[5] || v[6] || v[7];
mem++)
v &= *(mem);
return mem;
}
SSE4.2有PTEST
指令允许运行类似for
条件的测试,但GCC生成的代码只是解包向量并逐个检查单个元素:
.L2:
vandps (%rax), %ymm1, %ymm1
vmovdqa %xmm1, %xmm0
addq $32, %rax
vmovd %xmm0, %edx
testl %edx, %edx
jne .L2
vpextrd $1, %xmm0, %edx
testl %edx, %edx
jne .L2
vpextrd $2, %xmm0, %edx
testl %edx, %edx
jne .L2
vpextrd $3, %xmm0, %edx
testl %edx, %edx
jne .L2
vextractf128 $0x1, %ymm1, %xmm0
vmovd %xmm0, %edx
testl %edx, %edx
jne .L2
vpextrd $1, %xmm0, %edx
testl %edx, %edx
jne .L2
vpextrd $2, %xmm0, %edx
testl %edx, %edx
jne .L2
vpextrd $3, %xmm0, %edx
testl %edx, %edx
jne .L2
vzeroupper
ret
有没有办法让GCC在不恢复使用内在函数的情况下为其生成有效的测试?
更新:作为参考,代码使用内置于(V)PTEST
的不可移植的GCC:
typedef uint32_t v8ui __attribute__ ((vector_size (32)));
typedef long long int v4si __attribute__ ((vector_size (32)));
const v8ui ones = { 1, 1, 1, 1, 1, 1, 1, 1 };
v8ui*
foo(v8ui *mem) {
v8ui v;
for ( v = ones;
!__builtin_ia32_ptestz256((v4si)v,
(v4si)ones);
mem++)
v &= *(mem);
return mem;
}
答案 0 :(得分:2)
gcc 4.9.2 -O3 -mavx2
(在64位模式下)没有意识到它可以ptest
使用||
或|
。
|
版本使用vmovd
和vpextrd
提取向量元素,并将内容与32位寄存器之间的7 or
个insn组合在一起。所以它非常糟糕,并没有利用任何仍会产生相同逻辑真值的简化。
||
版本同样糟糕,并且每次都提取相同的元素,但每个版本都会test
/ jne
。< / p>
所以在这一点上,你不能依靠GCC识别这样的测试并做任何远程高效的事情。 (pcmpeq
/ movmsk
/ test
是另一个不会很糟糕的序列,但gcc也不能生成它。)
答案 1 :(得分:1)
不会对vptest有帮助吗?如果您正在考虑性能,有时您会对本机类型提供的内容感到惊讶。下面是一些使用vanilla memcmp()的代码以及vptest指令(通过相应的内部函数使用)。我没有时间这些功能。
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <immintrin.h>
typedef uint32_t v8ui __attribute__ ((vector_size (32)));
v8ui*
foo1(v8ui *mem)
{
v8ui v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 };
if (memcmp(mem, &v, sizeof (v8ui)) == 0) {
printf("Ones\n");
} else {
printf("NOT Ones\n");
}
return mem;
}
v8ui*
foo2(v8ui *mem)
{
v8ui v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 };
__m256i a, b;
a = _mm256_loadu_si256((__m256i *)(&v));
b = _mm256_loadu_si256((__m256i *)(&mem));
if (!_mm256_testz_si256(a, b)) {
printf("NOT Ones\n");
} else {
printf("Ones\n");
}
return mem;
}
int
main()
{
v8ui v = (v8ui){ 1, 1, 1, 1, 1, 1, 1, 1 };
foo1(&v);
foo2(&v);
}
编译标志:
卫生署!直到现在我才发现你想让GCC在不使用内在函数的情况下生成vptest指令。无论如何我都会留下代码。
答案 2 :(得分:0)
如果编译器不够自动以自动生成优化,则有三个选项:
您已经使用gcc扩展自动排除了第一个选项,但llvm / clang可能会为您扩展这些扩展。
你已经公然排除了第二种选择。
第三种选择似乎是我最好的选择。 gcc是开源的,所以你可以制作(并提交)你自己的更改。如果您可以修改gcc以自动生成此优化(理想情况下来自100%标准C),那么您不仅可以实现生成此优化的目标,而且不会将crud引入您的程序,但您还将节省无数的手动优化(尤其是将来锁定你使用特定编译器的非标准版本。