我有以下代码(最小示例):
[a-z]+|"[a-z]+"|'[a-z]+'
使用#include <iostream>
#include <immintrin.h>
using namespace std;
int main(){
__m128i a = _mm_set_epi32(rand(),rand(),rand(),rand());
__m128i b = _mm_set_epi32(rand(),rand(),rand(),rand());
__m128i c = _mm_add_epi32(a,b);
int d[4];
_mm_storeu_si128((__m128i*)d,c);
cout<<d[0]<<endl;
cout<<d[1]<<endl;
cout<<d[2]<<endl;
cout<<d[3]<<endl;
return 0;
}
进行编译时,会产生一些奇怪/不好/效率低的汇编(https://godbolt.org/z/TQgbim)。它存储一次g++ -O3 -march=native
,然后进行对齐的加载并进行提取以进行元素访问(每次)。我可以看到为什么它需要将其存储到内存中,并且可以看到对齐的加载和提取效率如何,但是我看不到为什么提取后需要将相同的数据继续加载回xmm寄存器中。同样,当更改c
以便将其分配到堆(https://godbolt.org/z/Pk7qP2)时,它甚至不再执行对齐的加载,它只是将d
当作普通数组对待,以这种方式访问元素。有人可以解释为什么这样做,以及它可能带来什么好处吗?谢谢。
答案 0 :(得分:5)
是的,这是一个有趣的错过优化。
看起来它决定将向量存储/标量重载优化为向量提取,这通常很好。
但是这样做是在不考虑调用约定的情况下进行的,该调用约定没有保留调用的向量寄存器。例如,在Windows x64上可以使用xmm6,此代码就可以了。
如果您调用内联的函数,或者将所有4个元素作为args传递给同一函数,则此代码也很好。 (例如printf
)。
GCC有多次通过,而在程序逻辑的GIMPLE表示上操作的与体系结构无关的中间通过有时无法利用直到寄存器分配时间才知道的全部详细信息。对于gcc来说,有些优化很困难,因为它根本无法看到它们。
顺便说一句,如果您关心该效率水平,请使用'\n'
而不是endl
。您无需在那里显式刷新cout
。