如何使用 libsimdpp 复制 XMM 寄存器通道的下部
Posted
技术标签:
【中文标题】如何使用 libsimdpp 复制 XMM 寄存器通道的下部【英文标题】:How to copy the lower part of the lanes of a XMM register with libsimdpp 【发布时间】:2018-09-21 13:08:49 【问题描述】:我正在使用libsimdpp 编写矢量化代码。似乎我找不到将少于所有通道从内存或从寄存器移动到另一个寄存器的方法。
例如,使用 _mm_move_sd
或 _mm_move_ss
内在函数(movsd
或 movss
操作码)可以在寄存器之间复制最低的一或两条通道,而其他通道保持不变。
你如何对 libsimdpp 做同样的事情?
【问题讨论】:
【参考方案1】:我不熟悉 libsimdpp 库,但据我所知,您可以使用带有适当排列索引的 simdpp::shuffle2x2
来替代 _mm_move_sd
。从文件https://github.com/p12tic/libsimdpp/blob/master/simdpp/detail/insn/shuffle2x2.h 中,我们可以看到以这种方式使用_mm_shuffle_pd
或_mm_blend_pd
,这取决于所选择的排列索引。这些 Intel 内部函数可用作 _mm_move_sd
的替代品。
从manual page 看来,您似乎必须选择排列索引s0 = 2
和s1 = 1
来模拟_mm_move_sd
。这对应于
shuffle2x2.h
的第 156 和 157 行,即:if (s0 == 2 && s1 == 1) return _mm_blend_pd(b.native(), a.native(), 0x2);
。
_mm_move_ss
的替代品是 _mm_blend_ps
,带有合适的掩码。对于 simdpp::shuffle4x2
,libsimdpp 库在 x86 架构上选择 _mm_blend_ps
,如果排列索引 (s0==0 || s0==4) && (s1==1 || s1==5) && (s2==2 || s2==6) && (s3==3 || s3==7)
满足以下条件,请参阅 sse_float32_4x2.h,第 40 行和第 155 行。
请注意,例如,GCC 可以将_mm_blend_ps(a, b, 1)
和_mm_move_ss
编译为movss
指令。见this Godbolt link。因此,使用 libsimdpp 和智能编译器,确实可以生成movss
操作码。
但请注意,GCC 只识别 _mm_blend_pd(a, b, 1);
,而不是
_mm_blend_pd(a, b, 2);
,由 libsimdpp 生成。
请注意,正如 Peter Cordes 在评论中所说,自 Intel Haswell 处理器以来,在 Intel CPU 上,带有寄存器操作数的 blendpd/ps
指令比 movsd/ss
具有更好的吞吐量。
【讨论】:
blendpd
具有比movss xmx,xmm
更好的吞吐量(HSW 上的任何端口)(端口 5 仅在 NHM 和更高版本上);这是 gcc 对-march=haswell
的愚蠢去优化。也许 gcc 在 Core2 的日子里就引入了代码大小,并且从未重新检查过它:/
@PeterCordes:确实值得一提的是,blendps/ps
指令在现代 cpu 上的性能优于 movsd/ss
指令在现代 cpu 上的性能。
对,对于 register-to-register,shuffle 基本上可以做任何类型的移动。但是内存注册呢?假设我需要从内存中的两个连续浮点数中加载通道 0 和 1,将通道 2 和 3 归零。_mm_move_sd
不会在一条指令中执行此操作吗?
抱歉,让我澄清一下 - _mm_move_sd
似乎只能在寄存器之间移动,我的意思是 _mm_load_sd
然后。
根据Agner Fog 的instruction tables 指令,带有内存加载操作数的movsd
是只有一个微操作,而带有内存操作数的blendps
是2 个微操作。这证实了movsd
指令更可取。在我看来,libsimdpp 的可移植性非常好,但英特尔内部函数为您提供了更多机会来编写最高效的代码。以上是关于如何使用 libsimdpp 复制 XMM 寄存器通道的下部的主要内容,如果未能解决你的问题,请参考以下文章