出于准确性原因,我经常需要使用双精度,但我想将结果存储为浮点数。什么是最佳方式?我目前正在使用:
上交所2:_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));
有什么改进的想法吗?
出于准确性原因,我经常需要使用双精度,但我想将结果存储为浮点数。什么是最佳方式?我目前正在使用:
上交所2:_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
是你唯一的选择。这些指令在现代英特尔上解码为 2 微指令;一个用于 FMA 端口,一个用于 shuffle 端口。(https://agner.org/optimize/)
存储结果很简单,某种形式的_mm_store/storeu
就是你想要的。
对于 128 位向量(导致 2x float
= 64 位),您没有完整的 128 位向量结果。您可以将两个混洗到一个 128 位向量中,但是自从 Sandybridge 以来,英特尔上的 FP 混洗吞吐量为每个时钟 1 个,最好将它们分开存储。
您不想存储movlps
向量movsd
的低 64 位float
;它节省了一个指令字节,并且 C 内在函数使用更少的转换。但不幸的是,它需要 a__m64*
而不是 a float*
,所以你仍然需要一个演员表:
_mm_storel_pi((__m64*)dst, _mm_cvtpd_ps(xmm) );
但是对于加载,您肯定希望movsd
避免对旧值的错误依赖。 movlps
加载合并到一个寄存器中;movsd
加载零扩展。实际上,cvtps2pd xmm, qword [mem]
如果您可以让编译器从内在函数中发出它,那么会为您解决这个问题。
由于类似的原因,可能很难安全地做到这一点pmovzxbw xmm, qword [mem]
:编译器无法将 qword 加载折叠到 pmovzx/sx 的内存操作数中:(将8 个字符从内存加载到 __m256 变量中作为打包单精度浮点数)