SSE2 从 golang 中的打包数据中提取浮点数

SSE2 extract float from packed data in golang

我正在用 Golang 编写一个汇编函数。为简化起见,假设我想执行以下功能:

func sseSumOfMinimums (d1, d2 [2]float64) float64

它将计算 d1[0]、d2[0] 的最小值以及 d1[1] 和 d2[1] 的最小值并计算总和

在装配中我这样做:

TEXT ·sseSum(SB), [=11=]-40
MOVUPD d1+0(FP), X0 // loading d1 to X0
MOVUPD d2+16(FP), X1 // loading d1 to X1
MINPD X0, X1 // compute pair minimums and store to X1
MOVSD X1, X2 // move first min to X2
// How do I move second float of X1 to X3?
ADDSD X2, X3
MOVSD X3, ret+32(FP)

我缺少的部分是如何将第二个标量从 X1 提取到 X3

Go 是否不保证堆栈对齐,因此您可以为 minpd 使用内存源操作数?

另外,我对围棋不熟悉;它的 float 真的是大多数语言(包括 x86 asm)调用 double 的 IEEE binary64 吗?看到源代码中的 float 和 asm.

中使用的 pd (packed double) 指令很奇怪

为此调用独立的手写 asm 函数的开销将高于让编译器使用标量 minsd 为单个对执行此操作。特别是 Go 糟糕的调用约定,在内存中传递参数并将 return 值存储到内存中。

与调用此函数相比,具有 LLVM 或 gcc 后端的优化 Go 编译器应该使用内联代码以更低的延迟和更少的吞吐量成本完成工作,即使使用下面给出的优化也是如此。或者,如果您幸运的话,编译器将为您使用 minpd


但是对于实际问题,在minpd x0, x1之后,你需要的是xmm1的横向求和。 Fastest way to do horizontal float vector sum on x86.

您应该使用 movaps 来复制 xmm 寄存器,即使您只关心低 64 位。 movsd x1, x2 合并到 xmm2 的低 64 位,创建对旧值的错误依赖并花费 shuffle uop。

minpd   x0, x1
movhps  x1, x0        // high 64 bits of xmm1  => low 64 of xmm0
addsd   x1, x0

您可以 movaps x1, x2unpckhpd x2,x2,但这会花费额外的 movapdmovaps,您可以使用 movhps.

(movaps / movupsmovapd 更短,代码更小,并且在所有 CPU 上完全等同于 movapd / movupd用于加载、存储和 reg-reg 副本。)