内在函数与下面的实际指令之间存在一些脱节。
AVX:
所有这 3 个都生成完全相同的指令,vperm2f128:
_mm256_permute2f128_pd()
_mm256_permute2f128_ps()
_mm256_permute2f128_si256()
唯一的区别是类型 - 在指令级别不存在。
vperm2f128 是 256 位浮点指令。在 AVX 中,没有“真正的”256 位整数 SIMD 指令。所以即使_mm256_permute2f128_si256() 是一个“整数”内在函数,它实际上只是语法糖:
_mm256_castpd_si256(
_mm256_permute2f128_pd(
_mm256_castsi256_pd(x),
_mm256_castsi256_pd(y),
imm
)
);
从整数域到 FP 域的往返 - 从而导致绕过延迟。尽管这看起来很丑陋,但只有在 AVX 领域才能做到这一点。
vperm2f128 不是获得这种治疗的唯一指令,我发现其中至少有 3 个:
-
vperm2f128 / _mm256_permute2f128_si256()
-
vextractf128 / _mm256_extractf128_si256()
-
vinsertf128 / _mm256_insertf128_si256()
总的来说,这些内在函数的用例似乎是将数据加载为 256 位整数向量,并将它们打乱成多个 128 位整数向量以进行整数计算。同样,您存储为 256 位向量的相反。
如果没有这些“hack”内在函数,您将需要使用大量强制转换内在函数。
无论哪种方式,一个称职的编译器都会尝试优化类型。因此,即使您使用 256 位整数加载,它也会生成浮点加载/存储和洗牌。这将旁路延迟的数量减少到只有一层。 (当你从 FP-shuffle 转到 128 位整数计算时)
AVX2:
AVX2 通过为所有内容(包括随机播放)添加适当的 256 位整数 SIMD 支持来消除这种疯狂。
vperm2i128 指令是新的,同时还有一个新的内在函数 _mm256_permute2x128_si256()。
这与_mm256_extracti128_si256() 和_mm256_inserti128_si256() 一起使您可以执行256 位整数SIMD,并且实际上完全保持在整数域中。
相同指令的整数 FP 版本之间的区别与旁路延迟有关。在较旧的处理器中,从 int FP 域移动数据存在延迟。虽然 SIMD 寄存器本身与类型无关,但硬件实现却不是。并且有额外的延迟来获得从 FP 指令到整数指令输入的数据输出。 (反之亦然)
因此,重要的是(从性能的角度来看)使用正确的指令类型来匹配正在操作的实际数据类型。
在最新的处理器(Skylake 和更高版本?)上,关于 shuffle 指令似乎不再存在 int/FP 绕过延迟。虽然指令集仍然具有这种区别,但使用不同“类型”执行相同操作的 shuffle 指令现在可能映射到同一个 uop。