使用内在函数将双 SSE2/AVX/AVX512 存储为浮点数的最佳方法
Posted
技术标签:
【中文标题】使用内在函数将双 SSE2/AVX/AVX512 存储为浮点数的最佳方法【英文标题】:Optimal way to store double SSE2/AVX/AVX512 as floats using intrinsics 【发布时间】:2018-10-18 09:45:30 【问题描述】:出于准确性原因,我经常需要使用双精度,但我想将结果存储为浮点数。什么是最佳方式?我正在使用:
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));
有什么改进的想法吗?
【问题讨论】:
我觉得你的代码没问题。您可以使用_mm_storel_pd
或_mm_storel_pi
代替_mm_store_sd
。根据 Agner Fogs 指令表,在英特尔 Skylake 上,它的速度与您的解决方案一样快。可能在一些老架构上有一点点不同,但是我没有去查。
感谢您的信息!
另见 Peter Cordes 的评论 here。
不同指令之间的指令字节数可能不同,在某些情况下对性能的影响可能非常小。另见Godbolt link。原则上编译器应该选择最好的指令。但是,Clang 更喜欢 movlps
,而 GCC 更喜欢 movlpd
。
【参考方案1】:
从 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
来存储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)
【讨论】:
以上是关于使用内在函数将双 SSE2/AVX/AVX512 存储为浮点数的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
给定一个 int 偏移向量,如何使用 AVX512 内在函数收集单个字节?