首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从GP regs加载xmm

从GP regs加载xmm
EN

Stack Overflow用户
提问于 2018-06-10 07:15:58
回答 1查看 409关注 0票数 3

假设您希望将raxrdx中的值加载到xmm寄存器中。

一种方法是:

代码语言:javascript
复制
movq     xmm0, rax
pinsrq   xmm0, rdx, 1

虽然它相当慢!有没有更好的方法?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-06-10 07:57:58

在最近的Intel或AMD上,您不会在延迟或uop计数方面做得更好(我主要查看了Agner Fog为Ryzen /Skylake编写的表格)。对于相同的端口,movq+movq+punpcklqdq也是3uops。

在英特尔/ AMD上,如果整数->向量的ALU端口上的周围代码瓶颈,则值得考虑将GP寄存器存储到临时位置,并通过16字节读取重新加载它们,这是最近英特尔的端口5。

在英特尔上,端口5的pinsrq x,r,imm为2 uop,端口5的movq xmm,r64也为1 uop。

movhps xmm, [mem]可以微熔断负载,但它仍然需要一个端口5ALUuop。因此,movq xmm0,rax / mov [rsp-8], rdx / movhps xmm0, [rsp-8]是3个融合的域微操作,其中2个需要在最新的英特尔上使用端口5。存储转发延迟使其延迟明显高于插入。

使用store / store / movdqa (较长的存储转发停顿,读取负载较大的两个较窄的存储)存储两个GP regs也是3uops,但这是避免任何端口5uop的唯一合理顺序。大约15个周期的延迟是如此之多,以至于无序执行很容易隐藏它。

对于YMM和/或更窄的元素,stores + reload更值得考虑,因为您可以将摊位摊销到更多的商店/它可以为您节省更多的随机操作。但它仍然不应该成为32位元素的首选策略。

对于较窄的元素,如果有一种将2个窄整数打包到一个64位整数寄存器中的单一uop方法,那就更好了,这样就可以更广泛地传输到XMM regs。但事实并非如此:Packing two DWORDs into a QWORD to save store bandwidth shld在英特尔SnB系列上只有1个uop,但需要一个寄存器顶部的输入。与PowerPC或ARM相比,x86的位域插入/提取指令非常弱,每次合并需要多个指令(存储/重新加载除外,并且每个时钟1的存储吞吐量很容易成为瓶颈)。

合并掩码可以合并,并且AVX512F - broadcast to a vector from an integer reg允许单uop插入。

根据http://instlatx64.atw.hu/的电子表格(取自IACA的uop数据),在Skylake-AVX512上将任意宽度的整数寄存器广播到x/y/zmm向量只需要1 port5 uop。

Agner似乎没有在KNL上测试整数源regs,但类似的VPBROADCASTMB2Q v,k (掩码寄存器源)是1uop。

已经设置了掩码寄存器的:总共只有2个uops

代码语言:javascript
复制
; k1 = 0b0010

vmovq         xmm0, rax           ; 1 uop p5             ; AVX1
vpbroadcastq  xmm0{k1}, rdx       ; 1 uop p5  merge-masking

我认为合并屏蔽即使对于ALU uops来说也是“免费的”。请注意,我们首先执行VMOVQ,这样我们就可以避免对它进行更长的EVEX编码。但是如果你在一个mask reg而不是0010中有0001,用vmovq xmm0{k1}, rax将它混合到一个未屏蔽的广播中。

通过设置更多的掩码寄存器,我们可以对每个执行1个reg:

代码语言:javascript
复制
vmovq         xmm0, rax                         2c latency
vpbroadcastq  xmm0{k1}, rdx   ; k1 = 0b0010     3c latency
vpbroadcastq  ymm0{k2}, rdi   ; k2 = 0b0100     3c latency
vpbroadcastq  ymm0{k3}, rsi   ; k3 = 0b1000     3c latency

(对于完整的ZMM向量,可以启动第二个dep链和vinserti64x4来组合256位的一半。这意味着只有3k个寄存器,而不是7个。它需要1个额外的混洗操作,但除非有一些软件流水线,否则在对向量进行任何操作之前,OoO exec可能无法隐藏7个合并= 21c的延迟。)

代码语言:javascript
复制
; high 256 bits: maybe better to start again with vmovq instead of continuing
vpbroadcastq  zmm0{k4}, rcx   ; k4 =0b10000     3c latency
... filling up the ZMM reg

根据Instlatx64电子表格的引用和其他来源,英特尔在SKX上列出的vpbroadcastq延迟仍然是3c,即使目的地只有xmm。http://instlatx64.atw.hu/

同一文档确实将vpbroadcastq xmm,xmm列为1c延迟,因此我们在合并依赖链中每一步获得3c延迟大概是正确的。不幸的是,合并屏蔽uop需要目标寄存器和其他输入一样早准备好;因此操作的合并部分不能单独转发。

k1 = 2 = 0b0010开始,我们可以用KSHIFT初始化其余部分

代码语言:javascript
复制
mov      eax, 0b0010 = 2
kmovw    k1, eax
KSHIFTLW k2, k1, 1
KSHIFTLW k3, k1, 2

#  KSHIFTLW k4, k1, 3
# ...

KSHIFT仅在端口5 (SKX)上运行,但KMOV也是如此;从整数寄存器中移动每个掩码只会消耗额外的指令来首先设置整数regs。

实际上,如果向量的高位字节填充了广播,而不是零,那么我们可以使用0b1110 / 0b1100等作为掩码。

我们最终会写出所有的元素。我们可以从KXNOR k0, k0,k0开始生成-1,然后左移,但这是2个port5 uop,而mov eax,2 / kmovw k1, eax是p0156 + p5。

不带掩码寄存器的合并::(没有kmov k1, imm,从内存加载需要多次uop,因此一次性使用合并掩码没有3-uop选项。但在循环中,如果您可以保留一些掩码规则,这似乎要好得多。)

代码语言:javascript
复制
VPBROADCASTQ  xmm1, rdx           ; 1 uop  p5      ; AVX512VL (ZMM1 for just AVX512F)
vmovq         xmm0, rax           ; 1 uop p5             ; AVX1
vpblendd      xmm0, xmm0, xmm1, 0b1100    ; 1 uop p015   ; AVX2

; SKX: 3 uops:  2p5 + p015
; KNL: 3 uops: ? + ? + FP0/1

这里唯一的好处是3个uop中的一个不需要端口5。

vmovsd xmm1, xmm1, xmm0也会混合这两个部分,但只在最近的英特尔端口5上运行,不像整数立即混合在任何矢量算术逻辑单元端口上运行。

关于整数->向量策略的更多讨论

gcc喜欢存储/重新加载,这在任何情况下都不是最优的,除非在非常罕见的端口5绑定的情况下,其中大量的延迟并不重要。我提交了https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80820https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80833,更多地讨论了32位或64位元素在各种体系结构上可能是最佳的。

在第一个bug中,我建议用AVX512替换上面的vpbroadcastq来插入。

(如果编译_mm_set_epi64x,一定要使用-mtune=haswell或更新的版本,以避免对默认mtune=generic进行糟糕的调优。或者,如果二进制文件仅在本地计算机上运行,请使用-march=native。)

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50779309

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档