gpt4 book ai didi

c++ - 是否有适用于 double (__m128d) 的 Move (_mm_move_ss) 和 Set (_mm_set_ss) 内在函数?

转载 作者:搜寻专家 更新时间:2023-10-31 00:29:56 25 4
gpt4 key购买 nike

多年来,我曾多次看到带有 float 参数的内部函数被转换为 __m128,代码如下:__m128 b = _mm_move_ss (m, _mm_set_ss(a));.

例如:

void MyFunction(float y)
{
__m128 a = _mm_move_ss(m, _mm_set_ss(y)); //m is __m128
//do whatever it is with 'a'
}

我想知道是否有类似的方法使用 _mm_move_mm_set 内在函数对 double (__m128d) 执行相同的操作?

最佳答案

几乎每个 _ss_ps内在/指令有一个double带有 _sd 的版本或 _pd后缀。 (标量 double 或压缩 double )。

例如搜索 (double in Intel's intrinsic finder找到采用 double 的内部函数作为第一个参数。或者只是找出最佳的 asm 是什么,然后在 insn ref 手册中查找这些指令的内在函数。除了它 doesn't list all the intrinsics for movsd , 因此在内部函数查找器中搜索指令名称通常是可行的。

re: header files: always just include <immintrin.h> .它包括所有英特尔 SSE/AVX 内在函数。


另见 ways to put a float into a vector , 和 标记 wiki 以获取有关如何打乱 vector 的链接。 (即 Agner Fog's optimizing assembly guide 中的洗牌指令表)

(请参阅下面的一些有趣的编译器输出的 godbolt 链接)

回复:你的序列

只使用_mm_move_ss (或 sd)如果你真的想合并两个 vector 。

你没有展示如何m被定义为。您对 a 的使用因为 float 和 vector 的变量名意味着 vector 中唯一有用的信息是 float精氨酸。变量名冲突当然意味着它无法编译。

不幸的是,似乎没有任何方法可以“转换”一个 floatdouble进入前 3 个元素中带有垃圾的 vector ,就像 __m128 一样-> __m256 :
__m256 _mm256_castps128_ps256 (__m128 a) .我发布了一个关于内部函数限制的新问题:How to merge a scalar into a vector without the compiler wasting an instruction zeroing upper elements? Design limitation in Intel's intrinsics?

我尝试使用 _mm_undefined_ps()为了实现这一点,希望这会在编译器中提示它可以将传入的高垃圾留在原地,在

// don't use this, it doesn't make better code
__m128d double_to_vec_highgarbage(double x) {
__m128d undef = _mm_undefined_pd();
__m128d x_zeroupper = _mm_set_sd(x);
return _mm_move_sd(undef, x_zeroupper);
}

但是clang3.8将它编译为

    # clang3.8 -O3 -march=core2
movq xmm0, xmm0 # xmm0 = xmm0[0],zero
ret

所以没有优势,仍然将上半部分归零而不是将其编译为 ret . gcc 实际上编写了非常糟糕的代码:

double_to_vec_highgarbage:  # gcc5.3 -march=nehalem
movsd QWORD PTR [rsp-16], xmm0 # %sfp, x
movsd xmm1, QWORD PTR [rsp-16] # D.26885, %sfp
pxor xmm0, xmm0 # __Y
movsd xmm0, xmm1 # tmp93, D.26885
ret

_mm_set_sd似乎是将标量转换为 vector 的最佳方式。

__m128d double_to_vec(double x) {
return _mm_set_sd(x);
}

clang 将其编译为 movq xmm0,xmm0 , gcc 存储/重新加载 -march=generic .


其他有趣的编译器输出 from the float and double versions on the Godbolt compiler explorer

float_to_vec:   # gcc 5.3 -O3 -march=core2
movd eax, xmm0 # x, x
movd xmm0, eax # D.26867, x
ret

float_to_vec: # gcc5.3 -O3 -march=nehalem
insertps xmm0, xmm0, 0xe # D.26867, x
ret

double_to_vec: # gcc5.3 -O3 -march=nehalem. It could still have use movq or insertps, instead of this longer-latency store-forwarding round trip
movsd QWORD PTR [rsp-16], xmm0 # %sfp, x
movsd xmm0, QWORD PTR [rsp-16] # D.26881, %sfp
ret

float_to_vec:   # clang3.8 -O3 -march=core2 or generic (no -march)
xorps xmm1, xmm1
movss xmm1, xmm0 # xmm1 = xmm0[0],xmm1[1,2,3]
movaps xmm0, xmm1
ret

double_to_vec: # clang3.8 -O3 -march=core2, nehalem, or generic (no -march)
movq xmm0, xmm0 # xmm0 = xmm0[0],zero
ret


float_to_vec: # clang3.8 -O3 -march=nehalem
xorps xmm1, xmm1
blendps xmm0, xmm1, 14 # xmm0 = xmm0[0],xmm1[1,2,3]
ret

所以 clang 和 gcc 对 float 使用不同的策略与 double ,即使他们可以使用相同的策略。

使用像 movq 这样的整数运算浮点运算之间会导致额外的旁路延迟延迟。使用 insertps将输入寄存器的上部元素归零应该是 float 或 double 的最佳策略,因此所有编译器应该在 SSE4.1 可用时使用它。 xorps + blend 也很好,并且可以在比 insertps 更多的端口上运行。存储/重新加载可能是最糟糕的,除非我们在 ALU 吞吐量上遇到瓶颈,并且延迟无关紧要。

关于c++ - 是否有适用于 double (__m128d) 的 Move (_mm_move_ss) 和 Set (_mm_set_ss) 内在函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38963947/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com