移动 __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、movqmovsd 内部函数应该可以高效编译。【参考方案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 的上下浮动的主要内容,如果未能解决你的问题,请参考以下文章

WinForm如何实现浮动效果%>_<%

clearfix(清除浮动)

浮动及清除浮动的方式

转移 __m128i 的最佳方式?

float浮动

css设置左浮动