使用内部函数将 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 的内存操作数中:()
出于准确性原因,我经常需要使用双精度,但我想将结果存储为浮点数。什么是最佳方式?我目前正在使用:
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 的内存操作数中:(