如何在 MSVC 中高效地将两个 __m128d 转换为一个 __m128i?

Posted

技术标签:

【中文标题】如何在 MSVC 中高效地将两个 __m128d 转换为一个 __m128i?【英文标题】:How to efficiently convert from two __m128d to one __m128i in MSVC? 【发布时间】:2016-09-15 04:24:00 【问题描述】:

转换然后移位然后按位或'ing 是从两个__m128d 转换为单个__m128i 的唯一方法吗?

这对于 Xcode 在 x64 构建中是完全可以接受的

m128d v2dHi = ....
m128d v2dLo = ....
__m128i v4i = _mm_set_epi64(_mm_cvtpd_pi32(v2dHi), _mm_cvtpd_pi32(v2dLo))

反汇编显示_mm_cvtpd_pi32 正在使用。但是,Visual Studio 无法编译它,抱怨链接器错误。这在 VS 文档中得到支持,说 _mm_cvtpd_pi32 在 x64 上不受支持。

我不太担心它不可用,但是是两次转换,一次移位,然后是按位 - 还是最快的方式?

【问题讨论】:

【参考方案1】:

如果您遇到链接器错误,您可能会忽略有关未声明的内部函数的警告。

您当前的代码很有可能被编译成糟糕的 asm。如果它编译为向量移位和 OR,则它已经编译为次优代码。 (更新:这不是它编译成的,IDK 你从哪里得到这个想法的。)

使用 2x _mm_cvtpd_epi32 获得两个 __m128i 向量,其中每个向量的低 2 个元素中包含所需的整数。使用 _mm_unpacklo_epi64 将这两个低半部分组合成一个包含所有 4 个元素的向量。


clang3.8.1 on the Godbolt compiler explorer 的编译器输出。 (我认为 Xcode 默认使用 clang)。

#include <immintrin.h>

// the good version
__m128i pack_double_to_int(__m128d a, __m128d b) 
    return _mm_unpacklo_epi64(_mm_cvtpd_epi32(a), _mm_cvtpd_epi32(b));

    cvtpd2dq        xmm0, xmm0
    cvtpd2dq        xmm1, xmm1
    punpcklqdq      xmm0, xmm1      # xmm0 = xmm0[0],xmm1[0]
    ret

// the original
__m128i pack_double_to_int_badMMX(__m128d a, __m128d b) 
    return _mm_set_epi64(_mm_cvtpd_pi32(b), _mm_cvtpd_pi32(a));

    cvtpd2pi        mm0, xmm1
    cvtpd2pi        mm1, xmm0
    movq2dq xmm1, mm0
    movq2dq xmm0, mm1
    punpcklqdq      xmm0, xmm1      # xmm0 = xmm0[0],xmm1[0]
      # note the lack of EMMS, because of not using the intrinsic for it
    ret

当 SSE2 及更高版本可用时,MMX 几乎完全没用;只是避免它。有关一些指南,请参阅sse 标签 wiki。

【讨论】:

Xcode 没有优化它。反汇编显示正在使用 _mm_cvtpd_pi32,而 _mm_set_epi64 只是使用 mov 来存储值。 是的,它有效:_mm_unpacklo_epi64(_mm_cvtpd_epi32(v2dLo), _mm_cvtpd_epi32(v2dHi))

以上是关于如何在 MSVC 中高效地将两个 __m128d 转换为一个 __m128i?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 gcc 中静态初始化 __m128i 数组?

用于 C++/SSE 代码的高效 NEON 内在函数

将未对齐的双精度数加载到 _m128d 寄存器中

将 __m128d 从 MASM 过程返回给 C 调用者

两个 __m128i 的两个位到一个 __m128i 的四个位 -SSE

将 __m256i 设置为两个 __m128i 值的值