如何让clang/gcc对循环数组比较进行矢量化


bool equal(uint8_t * b1,uint8_t * b2){
b1=(uint8_t*)__builtin_assume_aligned(b1,64);
b2=(uint8_t*)__builtin_assume_aligned(b2,64);
for(int ii = 0; ii < 64; ++ii){
if(b1[ii]!=b2[ii]){
return false;
}
}
return true;
}

从程序集来看,clang和gcc除了循环展开之外,似乎没有任何优化需要添加(带有标记-O3-mavx512f-msse4.2(。我认为将两个内存区域都放在512位寄存器中并进行比较是非常容易的。更令人惊讶的是,两个编译器都未能优化这一点(理想情况下只需要一个64位比较,不需要特殊的大寄存器(:

bool equal(uint8_t * b1,uint8_t * b2){
b1=(uint8_t*)__builtin_assume_aligned(b1,8);
b2=(uint8_t*)__builtin_assume_aligned(b2,8);
for(int ii = 0; ii < 8; ++ii){
if(b1[ii]!=b2[ii]){
return false;
}
}
return true;
}

那么,这两个编译器都是愚蠢的吗?或者这段代码没有矢量化是有原因的吗?除了编写内联程序集之外,还有什么方法可以强制矢量化吗?

"我假设";以下是最有效的

memcmp(b1, b2, any_size_you_need);

尤其是对于巨大的阵列!

(对于小型阵列,无论如何都没有太多好处!(

否则,您需要使用Intel Intrnsics手动向量化。(chtz也提到过。(我开始关注它,直到我想到memcmp

编译器必须假设,一旦函数返回(或退出循环(,它就无法读取当前索引后面的任何字节——例如,如果其中一个指针恰好指向无效内存边界附近的某个地方。您可以使用(非惰性(逐位&/|运算符来组合单个比较的结果,从而为编译器提供优化的机会:

bool equal(uint8_t * b1,uint8_t * b2){
b1=(uint8_t*)__builtin_assume_aligned(b1,64);
b2=(uint8_t*)__builtin_assume_aligned(b2,64);
bool ret = true;
for(int ii = 0; ii < 64; ++ii){
ret &= (b1[ii]==b2[ii]);
}
return ret;
}

Godbolt演示:https://godbolt.org/z/3ePh7q5rM

不过,gcc仍然无法将其矢量化。因此,如果这个函数对性能至关重要,您可能需要编写它的手动矢量化版本。

最新更新