使用内部函数将 double SSE2/AVX/AVX512 存储为浮点数的最佳方式

Optimal way to store double SSE2/AVX/AVX512 as floats using intrinsics

出于准确性原因,我经常需要使用双精度,但我想将结果存储为浮点数。什么是最佳方式?我目前正在使用:

SSE2:_mm_store_sd((double*)dst, _mm_castps_pd(_mm_cvtpd_ps(xmm)));

AVX:_mm_storeu_ps(dst, _mm256_cvtpd_ps(ymm));

AVX512:_mm256_storeu_ps(dst, _mm512_cvtpd_ps(zmm));

有什么改进的想法吗?

从 packed-double 到 packed-float 的转换仅适用于缩小形式,不适用于采用 2 个 double 向量并打包为 1 个 float 向量的版本。所以是的,[v]cvtpd2ps are your only option. These instructions decode to 2 uops on modern Intel; one for the FMA port(s) and one for the shuffle port. (https://agner.org/optimize/)

的内在函数

存储结果很简单,某种形式的 _mm_store/storeu 就是您想要的。


对于 128 位向量(产生 2x float = 64 位),您没有完整的 128 位向量结果。您可以将两个一起洗牌成一个 128 位向量,但是自从 Sandybridge 以来,英特尔的 FP 洗牌吞吐量为每时钟 1 个,最好将它们分开存储。

您希望 movlps 而不是 movsd 来存储 float 向量的低 64 位;它节省了一个指令字节,并且 C 内在函数的使用更少。但不幸的是它需要一个 __m64* 而不是 float*,所以你仍然需要一个转换:

_mm_storel_pi((__m64*)dst,   _mm_cvtpd_ps(xmm) );

但是对于加载,您肯定希望 movsd 避免对旧值的错误依赖。 movlps 负载合并到一个寄存器中; movsd 加载零扩展。实际上,cvtps2pd xmm, qword [mem] 会为您解决这个问题,如果您能让编译器从内在函数中发出它的话。

可能很难安全地做到这一点,原因与 pmovzxbw xmm, qword [mem] 类似:编译器无法将 qword 加载折叠到 pmovzx/sx 的内存操作数中:()