gpt4 book ai didi

assembly - 微融合和寻址模式

转载 作者:行者123 更新时间:2023-12-03 06:40:05 25 4
gpt4 key购买 nike

我使用Intel® Architecture Code Analyzer(IACA)发现了一些意外(对我而言)。

以下指令使用[base+index]寻址

addps xmm1, xmmword ptr [rsi+rax*1]

根据IACA不会微熔断。但是,如果我这样使用 [base+offset]
addps xmm1, xmmword ptr [rsi]

IACA报告说确实融合了。

Intel optimization reference manual的2-11节给出了以下示例,“所有解码器都可以处理的微融合微操作”
FADD DOUBLE PTR [RDI + RSI*8]

Agner Fog's optimization assembly manual还提供了使用 [base+index]寻址进行微操作融合的示例。参见,例如,第12.2节“Core2上的相同示例”。那么正确的答案是什么?

最佳答案

在解码器和uop缓存中,寻址模式不会影响微融合(除非带有立即数的指令不能对与RIP相关的寻址模式进行微融合)。
但是uop和寻址模式的某些组合无法在ROB中保持微融合(在乱序内核中),因此,英特尔SnB系列CPU在必要时(在问题/重命名阶段。对于问题吞吐量和无序窗口大小(ROB大小),重要的是取消分层后的融合域uop计数。
Intel's optimization manual在第2.5.2.4节:Micro-op队列和环路流检测器(LSD)中描述了Sandybridge的取消分层,但未描述任何后续的微体系结构的更改。
更新:现在,英特尔手册中有详细的部分描述了Haswell的分层。请参阅第2.4.5节“分层”。在2.5.2.4节中对SandyBridge进行了简要说明。

规则,据我从SnB,HSW和SKL的实验中可以看出来的最好:

  • SnB(我也假设IvB):索引寻址模式始终是非分层的,其他寻址模式则保持微融合。 IACA是正确的(大部分是?)。
  • HSW,SKL:仅当索引的ALU指令具有2个操作数并且将dst寄存器视为读-修改-写时,它们才会保留微融合索引。这里的“操作数”包括标志,这意味着adccmov不会微熔断。大多数VEX编码的指令也不会融合,因为它们通常具有三个操作数(因此paddb xmm0, [rdi+rbx]融合而vpaddb xmm0, xmm0, [rdi+rbx]不融合)。最后,偶尔的第2个操作数只写的操作数指令(例如pabsb xmm0, [rax + rbx])也不会融合。 IACA应用SnB规则是错误的。

  • 相关:简单(非索引)寻址模式是端口7(Haswell和更高版本)上的专用存储地址单元可以处理的唯一寻址模式,因此避免对存储区使用索引寻址模式仍然很有用。 (这样做的一个好技巧是用单个寄存器来寻址dst,而用 dst+(initial_src-initial_dst)来寻址src。然后,您只需要在循环内增加dst寄存器。)
    请注意,有些指令根本不会微熔丝(即使在解码器/ uop-cache中也是如此)。例如 shufps xmm, [mem], imm8vinsertf128 ymm, ymm, [mem], imm8在通过Skylake的SnB上始终为2 uops,即使其寄存器源版本仅为1 uop。这对于带有imm8控制操作数加上通常的dest / src1,src2寄存器/内存操作数的指令来说是典型的,但是还有其他几种情况。例如 PSRLW/D/Q xmm,[mem](来自内存操作数的 vector 移位计数)没有微熔丝,PMULLD也没有。
    另请参见 this post on Agner Fog's blog,以获取有关在读取大量寄存器时HSW / SKL的问题吞吐量限制的讨论: 与带有索引寻址模式的许多微融合相比,使用较少寄存器操作数的相同指令可能会导致性能下降:单寄存器寻址模式和立即。 我们尚不清楚原因,但我怀疑某种寄存器读取限制,可能与从PRF中读取大量冷寄存器有关。

    测试用例,实际测量中的数字:即使在以后未分层的情况下,这些也都属于解码器AFAIK中的微型 fuse 。
    # store
    mov [rax], edi SnB/HSW/SKL: 1 fused-domain, 2 unfused. The store-address uop can run on port7.
    mov [rax+rsi], edi SnB: unlaminated. HSW/SKL: stays micro-fused. (The store-address can't use port7, though).
    mov [buf +rax*4], edi SnB: unlaminated. HSW/SKL: stays micro-fused.

    # normal ALU stuff
    add edx, [rsp+rsi] SnB: unlaminated. HSW/SKL: stays micro-fused.
    # I assume the majority of traditional/normal ALU insns are like add
    HSW / SKL可能必须取消层压的三输入指令
    vfmadd213ps xmm0,xmm0,[rel buf] HSW/SKL: stays micro-fused: 1 fused, 2 unfused.
    vfmadd213ps xmm0,xmm0,[rdi] HSW/SKL: stays micro-fused
    vfmadd213ps xmm0,xmm0,[0+rdi*4] HSW/SKL: un-laminated: 2 uops in fused & unfused-domains.
    (So indexed addressing mode is still the condition for HSW/SKL, same as documented by Intel for SnB)

    # no idea why this one-source BMI2 instruction is unlaminated
    # It's different from ADD in that its destination is write-only (and it uses a VEX encoding)
    blsi edi, [rdi] HSW/SKL: 1 fused-domain, 2 unfused.
    blsi edi, [rdi+rsi] HSW/SKL: 2 fused & unfused-domain.


    adc eax, [rdi] same as cmov r, [rdi]
    cmove ebx, [rdi] Stays micro-fused. (SnB?)/HSW: 2 fused-domain, 3 unfused domain.
    SKL: 1 fused-domain, 2 unfused.

    # I haven't confirmed that this micro-fuses in the decoders, but I'm assuming it does since a one-register addressing mode does.

    adc eax, [rdi+rsi] same as cmov r, [rdi+rsi]
    cmove ebx, [rdi+rax] SnB: untested, probably 3 fused&unfused-domain.
    HSW: un-laminated to 3 fused&unfused-domain.
    SKL: un-laminated to 2 fused&unfused-domain.
    我认为Broadwell的行为类似于Skylake的ADC / cmov。
    奇怪的是,HSW取消了存储源ADC和CMOV的分层。也许英特尔在赶上Haswell交付截止日期之前并没有改变SnB的情况。
    Agner的insn表中说,在HSW / SKL上 cmovcc r,madc r,m根本没有微熔丝,但这与我的实验不符。我正在测量的周期计数与融合域uop发行计数匹配,出现了4 uops /时钟的发行瓶颈。希望他会再次检查并更正表格。
    内存目标整数ALU :
    add        [rdi], eax  SnB: untested (Agner says 2 fused-domain, 4 unfused-domain (load + ALU  + store-address + store-data)
    HSW/SKL: 2 fused-domain, 4 unfused.
    add [rdi+rsi], eax SnB: untested, probably 4 fused & unfused-domain
    HSW/SKL: 3 fused-domain, 4 unfused. (I don't know which uop stays fused).
    HSW: About 0.95 cycles extra store-forwarding latency vs. [rdi] for the same address used repeatedly. (6.98c per iter, up from 6.04c for [rdi])
    SKL: 0.02c extra latency (5.45c per iter, up from 5.43c for [rdi]), again in a tiny loop with dec ecx/jnz


    adc [rdi], eax SnB: untested
    HSW: 4 fused-domain, 6 unfused-domain. (same-address throughput 7.23c with dec, 7.19c with sub ecx,1)
    SKL: 4 fused-domain, 6 unfused-domain. (same-address throughput ~5.25c with dec, 5.28c with sub)
    adc [rdi+rsi], eax SnB: untested
    HSW: 5 fused-domain, 6 unfused-domain. (same-address throughput = 7.03c)
    SKL: 5 fused-domain, 6 unfused-domain. (same-address throughput = ~5.4c with sub ecx,1 for the loop branch, or 5.23c with dec ecx for the loop branch.)
    是的,没错, adc [rdi],eax / dec ecx / jnz的运行速度比SKL上的 add而不是 adc的循环快。我没有尝试使用其他地址,因为显然SKL不喜欢重复重写同一地址(存储转发延迟比预期的高。另请参见 this post about repeated store/reload to the same address being slower than expected on SKL
    内存目标 adc太多了,因为Intel P6系列(显然是SnB系列)不能为多uup指令的所有uo保持相同的TLB条目,所以 needs an extra uop to work around the problem-case where the load and add complete, and then the store faults, but the insn can't just be restarted because CF has already been updated。来自Andy Glew(@krazyglew)的一系列有趣的评论。
    大概是在解码器中融合,然后取消分层,这使我们从 needing microcode ROM中省下来,从而从 adc [base+idx], reg的一条指令中产生了4个以上的融合域uops。

    为什么SnB系列取消的层压:
    Sandybridge简化了内部uop格式,以节省功率和晶体管(以及对使用物理寄存器文件进行的主要更改,而不是将输入/输出数据保留在ROB中)。 SnB系列CPU仅在乱序内核中仅允许有限数量的输入寄存器用于融合域uop。对于SnB / IvB,该限制为2个输入(包括标志)。对于HSW及更高版本,uop的限制为3个输入。我不确定内存目的地的 addadc是否充分利用了这一点,还是不确定英特尔是否必须通过一些说明使Haswell脱颖而出
    对于未融合域的uop,Nehalem和更早版本的输入限制为2个输入,但是ROB显然可以使用3个输入寄存器(非内存寄存器操作数,基数和索引)跟踪微融合的uop。

    因此,索引存储和ALU + load指令仍可以有效地进行解码(不必成为组中的第一个uop),并且无需在uop缓存中占用额外的空间,但是微融合的优点在本质上已经消失了紧密的循环。 “未分层”发生在每个周期4个融合域uops问题/退休宽度困惑的核心之前。融合域性能计数器(uops_issueed / uops_retired.retire_slots)对未分层后的融合域uops进行计数。
    英特尔对重命名器的描述(第2.3.3.1节:重命名器)暗示实际上是进行分层的是问题/重命名阶段,因此在28/56/64熔合中,仍然有可能进行分层的微融合-domain uop问题队列/循环缓冲区(又名IDQ)。
    TODO:测试一下。制作一个刚好适合循环缓冲区的循环。更改某些内容,以便在发行之前将其中一个uops解除分层,并查看它是否仍从循环缓冲区(LSD)运行,或者是否现在已从uop缓存(DSB)中重新获取所有uops。有perf计数器可以跟踪uops的来源,因此这应该很容易。
    难度更大的TODO:如果在从uop缓存读取到添加到IDQ之间发生分层,请测试它是否可以减少uop缓存带宽。还是如果取消叠层恰好在发行阶段发生,是否会损害发行量? (即,发出第一个4后,它如何处理剩余的uops。)

    (请参见此答案的先前版本,以基于调整一些LUT代码的猜测,有关 vpgatherdd的一些注释比 pinsrw循环多大约1.7倍。)
    SnB的实验测试
    HSW / SKL编号是在i5-4210U和i7-6700k上测量的。两者都启用了HT(但系统处于空闲状态,因此线程拥有整个内核)。我在两个系统上都使用 ocperf.py在SKL上的Linux 4.10和HSW上的Linux 4.8上运行了相同的静态二进制文件。 (HSW便携式计算机NFS安装在我的SKL桌面的/ home上。)
    如下所述,在不再工作的i5-2500k上测量了SnB数。
    通过使用性能计数器进行uops和周期测试进行确认。
    我找到了 a table of PMU events for Intel Sandybridge,可与Linux的 perf命令一起使用。 (遗憾的是,标准 perf没有针对大多数特定于硬件的PMU事件(例如uops)的符号名称。)我在 recent answer中使用了它。
    ocperf.py provides symbolic names for these uarch-specific PMU events,因此您不必查找表。同样,相同的符号名称可跨多个架构使用。当我第一次写这个答案时,我并不知道。
    为了测试uop微融合,我构建了一个测试程序,该程序被限制为Intel CPU的每周期4 uop融合域限制。为了避免任何执行端口争用,这些微指令中的许多都是 nop,它们仍然位于uop缓存中,并且与其他任何uop一样,都通过管道进行传递,只是它们没有被调度到执行端口。 ( xor x, same或消除的 Action 将是相同的。)
    测试程序: yasm -f elf64 uop-test.s && ld uop-test.o -o uop-test
    GLOBAL _start
    _start:
    xor eax, eax
    xor ebx, ebx
    xor edx, edx
    xor edi, edi
    lea rsi, [rel mydata] ; load pointer
    mov ecx, 10000000
    cmp dword [rsp], 2 ; argc >= 2
    jge .loop_2reg

    ALIGN 32
    .loop_1reg:
    or eax, [rsi + 0]
    or ebx, [rsi + 4]
    dec ecx
    nop
    nop
    nop
    nop
    jg .loop_1reg
    ; xchg r8, r9 ; no effect on flags; decided to use NOPs instead

    jmp .out

    ALIGN 32
    .loop_2reg:
    or eax, [rsi + 0 + rdi]
    or ebx, [rsi + 4 + rdi]
    dec ecx
    nop
    nop
    nop
    nop
    jg .loop_2reg

    .out:
    xor edi, edi
    mov eax, 231 ; exit(0)
    syscall

    SECTION .rodata
    mydata:
    db 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
    我还发现,如果循环不是4微秒的倍数,则循环缓冲区外的uop带宽不是恒定的4。 (即 abcabc,...;不是 abcabcab,...)。不幸的是,Agner Fog的microarch文档尚不清楚循环缓冲区的这一限制。有关HSW / SKL的更多调查,请参见 Is performance reduced when executing loops whose uop count is not a multiple of processor width?。在这种情况下,SnB可能比HSW差,但是我不确定,并且仍然没有可用的SnB硬件。
    我想避免宏融合(比较和分支),所以我在 nop和分支之间使用了 dec。我使用了4个 nop,所以使用微融合时,循环将为8微码,并且每1次迭代以2个周期填充管道。
    在该循环的另一个版本中,使用不进行微熔丝的2操作数寻址模式,该循环将是10个融合域uops,并以3个周期运行。
    来自我的3.3GHz Intel Sandybridge(i5 2500k)的结果。 我没有做任何事情来让cpufreq调控器在测试之前提高时钟速度,因为周期是您不与内存进行交互时的周期。我为必须以十六进制形式输入的性能计数器事件添加了注释。
    测试1-reg寻址模式:无cmdline arg
    $ perf stat -e task-clock,cycles,instructions,r1b1,r10e,r2c2,r1c2,stalled-cycles-frontend,stalled-cycles-backend ./uop-test

    Performance counter stats for './uop-test':

    11.489620 task-clock (msec) # 0.961 CPUs utilized
    20,288,530 cycles # 1.766 GHz
    80,082,993 instructions # 3.95 insns per cycle
    # 0.00 stalled cycles per insn
    60,190,182 r1b1 ; UOPS_DISPATCHED: (unfused-domain. 1->umask 02 -> uops sent to execution ports from this thread)
    80,203,853 r10e ; UOPS_ISSUED: fused-domain
    80,118,315 r2c2 ; UOPS_RETIRED: retirement slots used (fused-domain)
    100,136,097 r1c2 ; UOPS_RETIRED: ALL (unfused-domain)
    220,440 stalled-cycles-frontend # 1.09% frontend cycles idle
    193,887 stalled-cycles-backend # 0.96% backend cycles idle

    0.011949917 seconds time elapsed
    测试2-reg寻址模式:使用cmdline arg
    $ perf stat -e task-clock,cycles,instructions,r1b1,r10e,r2c2,r1c2,stalled-cycles-frontend,stalled-cycles-backend ./uop-test x

    Performance counter stats for './uop-test x':

    18.756134 task-clock (msec) # 0.981 CPUs utilized
    30,377,306 cycles # 1.620 GHz
    80,105,553 instructions # 2.64 insns per cycle
    # 0.01 stalled cycles per insn
    60,218,693 r1b1 ; UOPS_DISPATCHED: (unfused-domain. 1->umask 02 -> uops sent to execution ports from this thread)
    100,224,654 r10e ; UOPS_ISSUED: fused-domain
    100,148,591 r2c2 ; UOPS_RETIRED: retirement slots used (fused-domain)
    100,172,151 r1c2 ; UOPS_RETIRED: ALL (unfused-domain)
    307,712 stalled-cycles-frontend # 1.01% frontend cycles idle
    1,100,168 stalled-cycles-backend # 3.62% backend cycles idle

    0.019114911 seconds time elapsed
    因此,两个版本都运行8000万条指令,并向执行端口分派(dispatch)了60M指令。 (带有内存源的 or会为 or调度到ALU,并为加载分配一个加载端口,无论它是否在管道的其余部分中进行了微融合。 nop根本不会调度到执行端口。)同样,这两个版本都将退回100M未融合域的uops,因为这里有40M的nops计数。
    区别在于融合域的计数器。
  • 1寄存器地址版本仅发行和退出80M融合域uops。这与指令数相同。每个insn变成一个融合域的uop。
  • 2寄存器地址版本发出100M融合域uops。这与未融合域的数量相同,表明未发生微融合。

  • 我怀疑如果分支的错误预测导致发行后但在退休之前被取消,则您只会看到UOPS_ISSUED和UOPS_RETIRED(使用退休插槽)之间的差异。
    最后,性能影响是真实的。 非融合版本花费了1.5倍的时钟周期。与大多数实际情况相比,这会夸大性能差异。循环必须运行一个完整的周期,另外2个额外的uops将其从2推到3。通常,额外的2个融合域的uop会产生较小的差异。如果代码不是每个周期4个融合域-uops所阻塞的,则可能没有区别。
    但是,如果使用适度的展开和递增多个指针(与简单的 [base + immediate offset]寻址一起使用)而不是使用 [base + index]寻址模式来实现,则在循环中进行大量内存引用的代码可能会更快。
    进一步的东西
    与即时RIP相对不能微融合。 Agner Fog的测试表明,即使在解码器/ uop缓存中也是如此,因此它们从不融合(而不是分层)。
    IACA弄错了,并声称这两种微型 fuse :
    cmp dword  [abs mydata], 0x1b   ; fused counters != unfused counters (micro-fusion happened, and wasn't un-laminated).  Uses 2 entries in the uop-cache, according to Agner Fog's testing
    cmp dword [rel mydata], 0x1b ; fused counters ~= unfused counters (micro-fusion didn't happen)
    当没有即时消息时,RIP-rel会进行微熔断(并保持熔断),例如:
    or  eax, dword  [rel mydata]    ; fused counters != unfused counters, i.e. micro-fusion happens

    微融合不会增加指令的延迟。负载可以在其他输入准备好之前发出。
    ALIGN 32
    .dep_fuse:
    or eax, [rsi + 0]
    or eax, [rsi + 0]
    or eax, [rsi + 0]
    or eax, [rsi + 0]
    or eax, [rsi + 0]
    dec ecx
    jg .dep_fuse
    由于 eax dep链,此循环每次迭代以5个周期运行。不比 or eax, [rsi + 0 + rdi]mov ebx, [rsi + 0 + rdi] / or eax, ebx序列快。 (未融合版本和 mov版本都运行相同数量的uops。)调度/ dep检查在未融合域中进行。新发行的微指令进入调度程序(又称为预留站(RS))以及ROB。他们在调度后离开调度程序(又被发送到执行单元),但留在ROB中直到退休。因此,用于隐藏负载延迟的无序窗口至少是调度程序的大小(在Skylake中为 54 unfused-domain uops in Sandybridge, 60 in Haswell,为97)。
    微融合没有将基址和偏移量设为同一寄存器的捷径。带有 or eax, [mydata + rdi+4*rdi]的循环(其中rdi为零)运行的次数和周期与带有 or eax, [rsi+rdi]的循环一样多。此寻址模式可用于迭代从固定地址开始的奇数大小的结构数组。这可能在大多数程序中从未使用过,因此毫不奇怪,英特尔没有花费晶体管来允许这种特殊情况下的2寄存器模式进行微熔丝。 (而且无论如何,在需要寄存器和比例因子的情况下,英特尔将其记录为“索引寻址模式”。)
    cmp / jccdec / jcc的宏融合创建了一个uop,即使在未融合域中也保持为单个uop。 dec / nop / jge仍然可以在单个周期中运行,但是只有三个,而不是一个。

    关于assembly - 微融合和寻址模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26046634/

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