55

使用英特尔® 架构代码分析器(IACA) ,我发现了一些意想不到的东西(对我来说)。

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

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

根据 IACA,不进行微熔断。但是,如果我[base+offset]这样使用

addps xmm1, xmmword ptr [rsi]

IACA 报告说它确实融合了。

英特尔优化参考手册的第 2-11 节以“所有解码器可以处理的微融合微操作”为例给出以下内容

FADD DOUBLE PTR [RDI + RSI*8]

Agner Fog 的优化组装手册也给出了使用[base+index]寻址的微操作融合示例。例如,参见第 12.2 节“Core2 上的相同示例”。那么正确答案是什么?

4

4 回答 4

45

在解码器和 uop-cache 中,寻址模式不影响微融合(除了具有立即操作数的指令不能微融合 RIP 相对寻址模式)。

但是 uop 和寻址模式的某些组合不能在 ROB 中保持微融合(在无序内核中),因此英特尔 SnB 系列 CPU 在必要时会在问题之前的某个时间“取消层压”/重命名阶段。对于问题吞吐量和无序窗口大小(ROB 大小),未分层后的融合域 uop 计数是最重要的。

Intel 的优化手册在第 2.5.2.4 节:Micro-op Queue and the Loop Stream Detector (LSD)中描述了Sandybridge 的 un-lamination ,但没有描述任何后续微架构的更改。

更新:现在英特尔手册有一个详细的部分来描述 Haswell 的非层压。请参阅第 2.4.5 节去层压。SandyBridge 的简要说明在第 2.5.2.4 节中。


规则,我从 SnB、HSW 和 SKL 的实验中可以看出:

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

相关:简单(非索引)寻址模式是端口7(Haswell 及更高版本)上的专用存储地址单元可以处理的唯一寻址模式,因此避免存储的索引寻址模式仍然可能有用。(一个很好的技巧是用一个寄存器来寻址你的 dst,但是用 src 来寻址dst+(initial_src-initial_dst)。然后你只需要在循环内增加 dst 寄存器。)

请注意,某些指令根本不会微融合(即使在解码器/uop-cache 中)。例如shufps xmm, [mem], imm8,或vinsertf128 ymm, ymm, [mem], imm8,通过 Skylake 在 SnB 上始终为 2 微指令,即使它们的寄存器源版本仅为 1 微指令。这对于具有 imm8 控制操作数加上通常的 dest/src1、src2 寄存器/内存操作数的指令来说是典型的,但还有一些其他情况。例如PSRLW/D/Q xmm,[mem](来自内存操作数的向量移位计数)不会进行微融合,PMULLD 也不会。

另请参阅Agner Fog 博客上的这篇文章,讨论有关读取大量寄存器时 HSW/SKL 问题吞吐量限制的讨论:与索引寻址模式的大量微融合相比,使用较少寄存器操作数的相同指令会导致速度减慢:一个-寄存器寻址模式和立即数。 我们还不知道原因,但我怀疑某种寄存器读取限制,可能与从 PRF 读取大量冷寄存器有关。


测试用例,来自实际测量的数字:这些都是解码器中的微熔丝,AFAIK,即使它们后来没有层压。

# 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 在 adc/cmov 中的行为类似于 Skylake。

奇怪的是,HSW 不分层内存源 ADC 和 CMOV。也许英特尔在交付 Haswell 的最后期限之前没有考虑将其从 SnB 更改。

Agner 的 insn 表说cmovcc r,m根本adc r,m不要在 HSW/SKL 上进行微融合,但这与我的实验不符。我测量的周期计数与融合域 uop 问题计数相匹配,对于 4 uop / 时钟问题瓶颈。希望他会仔细检查并更正表格。

内存目标整数 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运行速度使用 SKL 的相同循环快。我没有尝试使用不同的地址,因为显然 SKL 不喜欢重复重写同一地址(存储转发延迟高于预期。另请参阅这篇文章,关于重复存储/重新加载到同一地址比 SKL 上的预期慢.dec ecxjnzaddadc

内存目标adc是如此之多的微指令,因为英特尔 P6 系列(显然是 SnB 系列)无法为多微指令指令的所有微指令保留相同的 TLB 条目,因此它需要一个额外的微指令来解决这个问题 -加载和添加完成的情况,然后存储出现故障,但由于 CF 已经更新,因此无法重新启动 insn。来自 Andy Glew (@krazyglew) 的一系列有趣评论。

据推测,解码器中的融合和之后的非分层使我们无需微码 ROM来从adc [base+idx], reg.


为什么 SnB 系列不层压

Sandybridge 简化了内部 uop 格式以节省功耗和晶体管(同时对使用物理寄存器文件进行了重大更改,而不是将输入/输出数据保存在 ROB 中)。SnB 系列 CPU 仅允许有限数量的输入寄存器用于无序内核中的融合域 uop。对于 SnB/IvB,该限制为 2 个输入(包括标志)。对于 HSW 及更高版本,uop 的限制为 3 个输入。我不确定 memory-destination是否充分利用了这一点addadc或者英特尔是否必须通过一些说明让 Haswell 出局

Nehalem 和更早的版本对非融合域微指令有 2 个输入的限制,但 ROB 显然可以使用 3 个输入寄存器(非内存寄存器操作数、基址和索引)跟踪微融合微指令。


所以索引存储和 ALU+load 指令仍然可以高效解码(不必是组中的第一个 uop),并且不会在 uop 缓存中占用额外的空间,但除此之外,微融合的优势基本上没有了调优紧密的循环。 “un-lamination”发生在 4-fused-domain-uops-per-cycle issue/retire width out-of-order core 之前。融合域性能计数器 (uops_issued / uops_retired.retire_slots) 计算未分层后的融合域 uops。

英特尔对重命名器的描述(第 2.3.3.1 节:重命名器)暗示实际上是问题/重命名阶段进行了 un-lamination,因此用于 un-lamination 的 uops 仍可能在 28/56/64 fused 中进行微融合-domain uop 问题队列/循环缓冲区(又名 IDQ)。

TODO:测试一下。制作一个几乎不适合循环缓冲区的循环。改变一些东西,使其中一个微指令在发布之前不分层,并查看它是否仍然从循环缓冲区 (LSD) 运行,或者现在是否所有微指令都从微指令缓存 (DSB) 中重新获取。有性能计数器来跟踪 uops 的来源,所以这应该很容易。

更难的 TODO:如果在从 uop 缓存读取和添加到 IDQ 之间发生非分层,测试它是否可以减少 uop 缓存带宽。或者,如果在发行阶段发生未分层,是否会影响发行吞吐量?(即在发出前 4 个之后,它如何处理剩余的 uops。)


(有关基于调整一些 LUT 代码的一些猜测,请参阅此答案的先前版本,其中一些注释vpgatherdd表明周期比pinsrw循环多 1.7 倍。)

SnB 的实验测试

HSW/SKL 数值是在 i5-4210U 和 i7-6700k 上测量的。两者都启用了 HT(但系统空闲,因此线程拥有整个核心)。我在两个系统上运行相同的静态二进制文件,SKL 上的 Linux 4.10 和 HSW 上的 Linux 4.8,使用ocperf.py. (HSW 笔记本 NFS 安装了我的 SKL 桌面的 /home。)

如下所述在不再工作的 i5-2500k 上测量 SnB 数。

通过对微指令和周期的性能计数器进行测试来确认。

我找到了 Intel Sandybridge 的 PMU 事件表,用于 Linux 的perf命令。(perf不幸的是,对于大多数特定于硬件的 PMU 事件,标准没有符号名称,例如 uops。)我在最近的回答中使用了它。

ocperf.py为这些特定于 uarch 的 PMU 事件提供符号名称,因此您不必查找表。此外,相同的符号名称适用于多个 uarches。当我第一次写这个答案时,我并没有意识到这一点。

为了测试 uop 微融合,我构建了一个测试程序,该程序在 Intel CPU 的 4-uops-per-cycle fused-domain 限制上遇到了瓶颈。为了避免任何执行端口争用,这些 uop 中的许多都是nops,它们仍然位于 uop 缓存中,并与任何其他 uop 一样通过管道,除了它们不会被分派到执行端口。(一个xor x, same或一个被淘汰的动作是一样的。)

测试程序: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 的倍数,则循环缓冲区外的 uop 带宽不是恒定的 4 每个周期。(即它是abc, abc, ...;不是abca, bcab, ...)。不幸的是,Agner Fog 的 microarch 文档并不清楚循环缓冲区的这种限制。请参阅执行 uop 计数不是处理器宽度倍数的循环时性能会降低吗?有关 HSW/SKL 的更多调查。在这种情况下,SnB 可能比 HSW 更差,但我不确定并且仍然没有可用的 SnB 硬件。

我想把宏融合(比较和分支)排除在外,所以我在分支和分支nop之间使用了 s dec。我使用了 4nop秒,因此使用微融合,循环将是 8 微秒,并且每 1 次迭代以 2 个周期填充管道。

在循环的另一个版本中,使用不进行微融合的 2 操作数寻址模式,循环将是 10 个融合域微指令,并在 3 个周期内运行。

我的 3.3GHz 英特尔 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

因此,两个版本都运行了 80M 指令,并向执行端口发送了 60M 微指令。(or内存源为 分配到 ALU,为加载分配or一个加载端口,无论它是否在管道的其余部分中进行了微融合。 nop根本不分派到执行端口。)类似地,两个版本都淘汰了 100M 的未融合域 uops,因为这里计算了 40M 的 nops。

不同之处在于融合域的计数器。

  1. 1 寄存器地址版本仅发布和淘汰 80M 融合域微指令。这与指令的数量相同。每个insn变成一个融合域uop。
  2. 2 寄存器地址版本发出 100M 融合域微指令。这与未融合域 uops 的数量相同,表明没有发生微融合。

我怀疑如果分支错误预测导致 uops 在发布后但在退休之前被取消,您只会看到 UOPS_ISSUED 和 UOPS_RETIRED(已使用退休槽)之间的差异。

最后,性能影响是真实存在的。 非融合版本需要 1.5 倍的时钟周期。与大多数实际情况相比,这夸大了性能差异。循环必须在整个周期内运行(在 LSD 不太复杂的 Sandybridge 上),并且 2 个额外的微指令将其从 2 推到 3。通常,额外的 2 个融合域微指令会产生较小的差异。如果代码被 4-fused-domain-uops-per-cycle 以外的东西阻塞,则可能没有区别。

[base + immediate offset]尽管如此,如果使用适度的展开和递增多个指针(这些指针用于简单寻址,而不是使用[base + index]寻址模式)来实现,那么在循环中进行大量内存引用的代码可能会更快。

进一步的东西


RIP-relative with a immediate can't micro-fuse。Agner Fog 的测试表明,即使在解码器/uop-cache 中也是如此,因此它们从一开始就不会融合(而不是未分层)。

IACA 弄错了,并声称这两个微型保险丝:

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)

cmp(对于/ ,微+宏融合都有更多限制jcc。TODO:写下来以测试内存位置。)

当没有立即数时,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 中直到退休。因此,隐藏加载延迟的无序窗口至少是调度程序的大小(Sandybridge 中的 54 个未融合域微指令,Haswell 中的 60 个,Skylake 中的 97 个)。

微融合没有将基数和偏移量作为同一个寄存器的快捷方式。带有 or eax, [mydata + rdi+4*rdi](其中 rdi 为零)的循环运行与带有 的循环一样多的微指令和循环or eax, [rsi+rdi]。这种寻址模式可用于迭代从固定地址开始的奇数大小的结构数组。这可能从未在大多数程序中使用过,因此英特尔没有花费晶体管来允许这种特殊情况下的 2 寄存器模式进行微熔断也就不足为奇了。(无论如何,英特尔将其记录为“索引寻址模式”,其中需要寄存器和比例因子。)


cmp/jccdec/的宏融合创建了jcc一个即使在未融合域中也保持为单个 uop 的 uop。 dec / nop / jge仍然可以在一个周期中运行,但是是三个微指令而不是一个。

于 2015-06-24T13:17:28.377 回答
11

注意:由于我写了这个答案,彼得也测试了 Haswell 和 Skylake,并将结果整合到上面接受的答案中(特别是,我在下面归因于 Skylake 的大部分改进似乎实际上已经出现在 Haswell 中)。您应该看到关于跨 CPU 行为概要的答案,并且这个答案(尽管没有错)主要具有历史意义。

我的测试表明,在Skylake至少1上,处理器完全融合了甚至复杂的寻址模式,这与 Sandybridge 不同。

也就是说,Peter 上面发布的代码的1-arg2-arg版本在相同数量的循环中运行,分派和退出的微指令数量相同。

我的结果:

性能计数器统计信息./uop-test

     23.718772      task-clock (msec)         #    0.973 CPUs utilized          
    20,642,233      cycles                    #    0.870 GHz                    
    80,111,957      instructions              #    3.88  insns per cycle        
    60,253,831      uops_executed_thread      # 2540.344 M/sec                  
    80,295,685      uops_issued_any           # 3385.322 M/sec                  
    80,176,940      uops_retired_retire_slots # 3380.316 M/sec                  

   0.024376698 seconds time elapsed

性能计数器统计信息./uop-test x

     13.532440      task-clock (msec)         #    0.967 CPUs utilized          
    21,592,044      cycles                    #    1.596 GHz                    
    80,073,676      instructions              #    3.71  insns per cycle        
    60,144,749      uops_executed_thread      # 4444.487 M/sec                  
    80,162,360      uops_issued_any           # 5923.718 M/sec                  
    80,104,978      uops_retired_retire_slots # 5919.478 M/sec                  

   0.013997088 seconds time elapsed

性能计数器统计信息./uop-test x x

     16.672198      task-clock (msec)         #    0.981 CPUs utilized          
    27,056,453      cycles                    #    1.623 GHz                    
    80,083,140      instructions              #    2.96  insns per cycle        
    60,164,049      uops_executed_thread      # 3608.645 M/sec                  
   100,187,390      uops_issued_any           # 6009.249 M/sec                  
   100,118,409      uops_retired_retire_slots # 6005.112 M/sec                  

   0.016997874 seconds time elapsed

我没有在 Skylake 上找到任何 UOPS_RETIRED_ANY 指令,只有显然是融合域的“退休插槽”家伙。

最后的测试 ( uop-test x x) 是 Peter 建议的一个变体,它使用 RIP-relativecmp和立即数,已知它不会微融合:

.loop_riprel
    cmp dword [rel mydata], 1
    cmp dword [rel mydata], 2
    dec ecx
    nop
    nop
    nop
    nop
    jg .loop_riprel

结果表明,每个周期额外的 2 个 uops 被发出和停用的 uops 计数器拾取(因此测试可以区分是否发生了融合)。

欢迎对其他架构进行更多测试!您可以在 github中找到代码(从上面的 Peter 复制)。


[1] ...也许还有一些介于 Skylake 和 Sandybridge 之间的其他架构,因为 Peter 只测试了 SB 而我只测试了 SKL。

于 2016-09-02T05:31:07.683 回答
6

没有 uop 缓存的旧 Intel 处理器可以进行融合,所以这可能是 uop 缓存的一个缺点。我现在没有时间测试这个,但是下次我更新我的测试脚本时,我会为 uop fusion 添加一个测试。您是否尝试过使用 FMA 指令?它们是唯一允许在未融合的微指令中包含 3 个输入依赖项的指令。

于 2015-07-12T05:49:16.577 回答
6

我现在查看了 Intel Sandy Bridge、Ivy Bridge、Haswell 和 Broadwell 的测试结果。我还没有在 Skylake 上进行测试。结果是:

  • 具有两个寄存器寻址和三个输入依赖项的指令可以正常融合。只要它们包含的数据不超过 32 位(或 2 * 16 位),它们就只在微操作缓存中占用一个条目。
  • 可以使用 Haswell 和 Broadwell 上的融合乘加指令来制作具有四个输入依赖项的指令。这些指令仍然融合到单个微操作中,并且在微操作缓存中只占用一个条目。
  • 超过 32 位数据的指令,例如 32 位地址和 8 位立即数数据仍然可以融合,但在微操作缓存中使用两个条目(除非 32 位可以压缩成 16 位有符号整数)
  • 即使偏移量和立即数都非常小,具有 rip-relative 寻址和立即数常量的指令也不会融合。
  • 在测试的四台机器上,所有结果都是相同的。
  • 这些测试是使用我自己的测试程序使用性能监控计数器在足够小以适合微操作缓存的循环上执行的。

您的结果可能与其他因素有关。我没有尝试使用 IACA。

于 2015-12-01T14:54:01.620 回答