使用内在函数将双SSE2 / AVX / AVX512存储为浮点数的最佳方法

时间:2018-10-18 09:45:31

标签: vectorization avx sse2 avx512

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

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));

有什么改进想法吗?

1 个答案:

答案 0 :(得分:2)

只能从缩小的格式转换为从packed-double转换为packet-float,不适用于需要将2个double向量打包为1个float向量的版本。因此,是的,[v]cvtpd2ps的内在函数是您唯一的选择。这些指令在现代Intel上解码为2微秒;一个用于FMA端口,另一个用于随机播放端口。 (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的内存操作数中:(Loading 8 chars from memory into an __m256 variable as packed single precision floats