移动 __m128 的上下浮动
Posted
技术标签:
【中文标题】移动 __m128 的上下浮动【英文标题】:move lower and upper floats of __m128 【发布时间】:2020-01-15 15:14:46 【问题描述】:如何将__m128
的上下浮动移动到两个__m128
中,并将其他半部分清零。
【问题讨论】:
【参考方案1】:由于您没有指定,我假设意图是使用 SSE 内在函数。我还假设至少 SSE2 可用,因为这基本上是 x86-64 设置的基线……
执行上述操作的一种非常直接的方法是通过_mm_move_epi64()
简单地复制输入的低 64 位并将高 64 位归零
__m128 lo2(__m128 x)
return _mm_castsi128_ps(_mm_move_epi64(_mm_castps_si128(x)));
并使用_mm_move_sd()
复制输入的高64位并从零开始复制低64位
__m128 hi2(__m128 x)
return _mm_castpd_ps(_mm_move_sd(_mm_castps_pd(x), _mm_setzero_pd()));
工作示例here
【讨论】:
为什么不在lo2
中使用_mm_move_epi64(x)
,在hi2
中使用_mm_move_sd(x, _mm_setzero_pd())
?
@scicyb 是的,我也刚刚意识到这一点。更新了我的答案……
使用 SSE4.1 可以更高效地使用 _mm_blend_ps
和 _mm_setzero_ps
;与movsd
相比,blendps
可以在 Intel CPU 中更多的执行端口上运行。出于某些原因,英特尔 CPU 将 movsd reg,reg
解码为仅在端口 5 上运行,就像随机播放一样。 agner.org/optimize 和 uops.info。 (或者当然andps
/andps
也可以释放端口5的压力,但代价是常数)。但是是的,+1、movq
和 movsd
内部函数应该可以高效编译。【参考方案2】:
_mm_movelh_ps()
和 _mm_movehl_ps()
怎么样,结合全零寄存器,完全符合您的要求?
#include <iostream>
#include <x86intrin.h>
void print_vec(__m128 a)
alignas(16) float res[4];
_mm_store_ps(res, a);
std::cout << res[0] << '\t' << res[1] << '\t' << res[2] << '\t' << res[3]
<< '\n';
int main()
__m128 vec = _mm_set_ps(4.0f, 3.0f, 2.0f, 1.0f);
__m128 lo = _mm_movelh_ps(vec, _mm_setzero_ps());
__m128 hi = _mm_movehl_ps(vec, _mm_setzero_ps());
std::cout << "Orig:\t";
print_vec(vec);
std::cout << "Lower:\t";
print_vec(lo);
std::cout << "Upper:\t";
print_vec(hi);
return 0;
编译并运行它会产生:
Orig: 1 2 3 4
Lower: 1 2 0 0
Upper: 0 0 3 4
【讨论】:
是的,您可以使用它们将低或高 qword 替换为零,但这些都是随机指令,因此仅在英特尔的 1 个执行端口上运行。 (与movq xmm0, xmm1
零扩展在 3 个向量 ALU 端口中的任何一个上运行的低半部分)。此外,movq
具有复制到新寄存器的优势,但您的方式需要额外的movaps
以避免破坏vec
。 (因为稍后需要将另一半归零)。
不过,在当前的英特尔 CPU 上,movhlps
将低半部分归零与 movsd
将低半部分归零一样有效(来自 @Michael Kenzel 的回答)。并节省 1 字节的代码大小,因为它是 SSE1 ps 而不是 SSE2 pd。
@PeterCordes 我想在实际使用程序中进行智能调度会有助于缓解端口压力?它们都被列为具有 1 的吞吐量和延迟,所以即使不是据我所知(当然,这不是很好),它也不会那么糟糕。
无序执行通常意味着指令顺序无关紧要,只有依赖模式。尽管 uops 确实先执行最旧的准备就绪,但关键(延迟)路径可能会因执行端口的资源冲突而延迟,而执行端口的指令不需要那么早运行。在一个循环中,您要么在一个特定端口(或一组端口)上出现瓶颈,要么在其他端口(通常是前端)上出现瓶颈;指令顺序对此无能为力。 movq xmm,xmm
在 Intel 上是 0.33c 延迟,可以追溯到 Core2,而在 Zen uops.info/html-instr/MOVQ_0F7E_XMM_XMM.html 上是 0.25
无论如何,这个答案是一个值得考虑的好主意,但在大多数情况下,对于几乎所有 CPU 的低半部分,它的效率不如movq
。 (在 Nehalem 上,如果延迟遇到瓶颈,来自 FP->int->FP 的旁路延迟可能会使 movq
成为一个糟糕的选择。没有其他 CPU 的旁路延迟高达 2 个周期。但在这种情况下,对于特定于 Nehalem您希望 SSE4.1 insertps
复制和归零而不需要归零寄存器的版本。以上是关于移动 __m128 的上下浮动的主要内容,如果未能解决你的问题,请参考以下文章