尽管混合 SSE 和 AVX 编码会受到臭名昭著的惩罚(请参阅为什么在 Skylake 上没有 VZEROUPPER 的情况下这个 SSE 代码会慢 6 倍?),但可能需要混合 128 位和 256 位操作。
可以通过始终使用 AVX 编码(即使是 128-but 操作)或在任何 SSE 编码之前添加
vzeroupper
来避免这种损失。
对于编译器生成的代码,如果启用了 AVX,编译器将假定 AVX 可用,并将使用 AVX 编码。对于每个可以外部调用的函数,编译器都会在末尾插入
vzeroupper
。
但是,MSVC 允许通过直接使用内在函数来生成未启用 AVX 的 AVX 代码(与其他一些编译器不同,它们需要启用 AVX 的选项才能使用 AVX 内在函数)。
如果在单个函数中使用两个内在函数,如何避免混合 SSE 和 AVX?
编译器将在第一个 AVX 内在函数之后使用 AVX 编码。 例如下面的函数:
void test1(__m256i* dest, char x, char y)
{
__m256i a = _mm256_broadcastw_epi16(_mm_cvtsi32_si128(x)); // movd then vpbroadcastw
__m256i b = _mm256_broadcastw_epi16(_mm_cvtsi32_si128(y)); // vmovd then vpbroadcastw
_mm256_store_si256(dest, _mm256_andnot_si256(a, b));
}
会将第一个
_mm_cvtsi32_si128
编码为 movd
,第二个 vmovd
。它会在最后插入vzeroupper
。
如果通过 AVX 寄存器获取参数,它将从一开始就使用 AVX 编码(这使用
__vectorcall
调用约定发生)。同理,如果返回__m256i
类型,则vzeroupper
不会插入到最后
这不适用于未优化的编译。使用
/Od
或不使用 /O...
选项,它将仅对任何指令使用最低级别的编码。对于未优化的编译,它也不会在末尾插入vzeroupper
。