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 -mavx 512 f-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;
}
那么,这两个编译器都是愚蠢的,还是有什么原因导致代码没有进行矢量化?有没有什么方法可以在编写内联汇编之前强制进行矢量化?
2条答案
按热度按时间ddhy6vgd1#
“我认为”以下是最有效的
尤其是对于大阵!
(For小数组,反正也没有太多收获!)
否则,您将需要使用英特尔Intrinsics手动矢量化。(chtz也提到过。)我开始研究这个问题,直到我想到了
memcmp
。igsr9ssn2#
编译器必须假设一旦函数返回(或退出循环),它就不能读取当前索引后面的任何字节--例如,如果其中一个指针碰巧指向了无效内存边界附近的某个地方。您可以给予编译器一个机会来优化这一点,方法是使用(非懒惰的)按位
&
/|
运算符来合并各个比较的结果:Godbolt演示:https://godbolt.org/z/3ePh7q5rM
但是gcc仍然无法矢量化这个函数,所以如果这个函数对性能很关键,你可能需要编写这个函数的手动矢量化版本。