【问题标题】:Do 32-bit and 64-bit registers cause differences in CPU micro architecture?32位和64位寄存器会导致CPU微架构的差异吗?
【发布时间】:2022-01-04 22:46:54
【问题描述】:

我试图将 Peter Cordes 在his answer 中提到的方法与“将 CPU 寄存器中的所有位设置为 1”的问题进行比较。

因此,我编写了一个基准测试,将所有 13 个寄存器设置为除 e/rspe/rbpe/rcx 之外的所有位 1。

代码如下。 times 32 nop 用于避免 DSB 和 LSD 影响。

mov ecx, 100000000
Align 32
.test3:
    times 32 nop
    mov rax,-1
    mov rbx,-1
    ;mov ecx,-1
    mov rdx,-1
    mov rdi,-1
    mov rsi,-1
    mov r8,-1
    mov r9,-1
    mov r10,-1
    mov r11,-1
    mov r12,-1
    mov r13,-1
    mov r14,-1
    mov r15,-1

    dec ecx
    jge .test3
    jmp .out

我测试了他提到的以下方法,Full code in here

mov e/rax, -1                   

xor eax, eax        
dec e/rax               

xor ecx, ecx        
lea e/rax, [rcx-1]  

or e/rax, -1            

为了使这个问题更简洁,我将在下表中使用group1 a (g1a) 替换mov eax,-1

number pattern test number
group1 a mov eax,-1 test 7
group1 b mov rax,-1 test3
group2 a xor eax, eax / dec eax test6
group2 b xor eax, eax / dec rax test2
group3 a xor ecx, ecx / lea eax, [rcx-1] test0
group3 b xor ecx, ecx / lea rax, [rcx-1] test-1(test00)
group4 a or eax,-1 test5
group4 b or rax,-1 test1

下表显示从第 1 组到第 3 组,当使用 64 位寄存器时,每个循环多出 1 个周期。

IDQ_UOPS_NOT_DELIVERED 也增加了,这可以解释循环次数的增加。 但这可以解释每个循环多 1 个循环的确切原因吗?

cycles MITE cycles(r1002479) MITE 4uops cycles (r4002479) IDQ UOPS NOT DELIVERED(r19c)
g1a 1,300,903,705 1,300,104,496 800,055,137 601,487,115
g1b 1,400,852,931 1,400,092,325 800,049,313 1,001,524,712
g2a 1,600,920,156 1,600,113,480 1,300,061,359 501,522,554
g2b 1,700,834,769 1,700,108,688 1,300,057,576 901,467,008
g3a 1,701,971,425 1,700,093,298 1,300,111,482 902,327,493
g3b 1,800,891,861 1,800,110,096 1,300,059,338 1,301,497,001
g4a 1,201,164,208 1,200,122,275 1,100,049,081 201,592,292
g4b 1,200,553,577 1,200,074,422 1,100,031,729 200,772,985

另外,g2a和g2b的端口分布是不同的,不像g1a和g1b(g1a和g1b在端口分布上是一样的),或者g3a和g3b。

如果我评论times 32 nop,这种现象就会消失。是否与 MITE 有关?

p0 p1 p2 p3 p4 p5 p6 p7
g1a 299,868,019 300,014,657 5,925 7,794 16,589 300,279,232 499,885,294 7,242
g1b 299,935,968 300,085,089 6,622 8,758 18,842 299,935,445 500,426,436 7,336
g2a 299,800,192 299,758,460 7,461 9,635 20,622 399,836,486 400,312,354 8,446
g2b 200,047,079 200,203,026 7,899 9,967 21,539 500,542,313 500,296,034 9,635
g3a 36,568 550,860,773 7,784 10,147 22,538 749,063,082 99,856,623 9,767
g3b 36,858 599,960,197 8,232 10,763 23,086 700,499,893 100,078,368 9,513
g4a 200,142,036 300,600,535 5,383 6,705 15,344 400,045,302 500,364,377 6,802
g4b 200,224,703 300,284,609 5,464 7,031 15,817 400,047,050 499,467,546 6,746

环境:intel i7-10700、ubuntu 20.04 和 NASM 2.14.02。

用英语解释这个对我来说有点困难。如果描述不清楚,请发表评论。

【问题讨论】:

  • 问题是什么?您是否尝试衡量较短和较长指令之间的差异?
  • times 32 nop 用于避免 DSB 和 LSD 影响。 - 意味着您正在对传统解码器 (MITE) 进行基准测试,因为这是前端的瓶颈.尤其是像 7 字节 mov rdx,-1 或 5 字节 mov edx,-1 这样的长指令。您标记了 [intel],但您使用的是什么特定的 CPU? Skylake 衍生的?我猜不是 Alder Lake 上的 E-core;它们在 L1I 缓存中具有更宽的解码和标记指令边界,而 SnB 系列 CPU 获取 16 字节块以进行传统解码。在 agner.org/optimize 上查看 Agner 的 microarch pdf
  • 总标题大多与The advantages of using 32bit registers/instructions in x86-64重复。 IDK 您正在寻找的答案有多具体,您使用更长或更短的指令创建了哪些解码瓶颈,但很明显,当平均长度 >= 4 左右时,使用更长的指令会消耗吞吐量,尽管 SKL 和以后有由于解码和发布/重命名之间的缓冲,5 个解码器可以弥补这一点。 (建立一些缓冲解码 5 nops / clock,然后在生产较少时吃掉它)
  • 哦,我明白了。预解码仅限于查看每个周期 16 个字节,并且可能仅来自连续的获取块。 (或者可能 fetch 本身是一个瓶颈,但是它和 pre-decode 之间的队列,所以 NOP 应该给它一些时间来赶上。)分支预测可以让 CPU 将不同 fetch 块的部分粘贴到一个 16 字节的 pre -解码组。但是,如果队列中有足够的内容,我认为实际的解码器本身可以查看更多的总字节数。对于较大的平均指令长度,问题通常出在预解码。
  • @PeterCordes Skylake 有 4 个解码器(每个周期最多可以向 IDQ 提供 5 微指令),每个周期最多可以预解码 5 条指令。

标签: assembly x86-64 intel cpu-architecture micro-optimization


【解决方案1】:

所有示例中的瓶颈都是预解码器。

我用我的模拟器 uiCA (https://uica.uops.info/, https://github.com/andreas-abel/uiCA) 分析了你的例子。它预测以下吞吐量,与您的测量结果非常匹配:

uiCA 生成的跟踪表提供了有关代码执行方式的一些见解。例如,对于 g1a,它会生成以下跟踪:

您可以看到,对于 32 个 nop,预解码器需要 8 个周期,而对于其余指令,它需要 5 个周期,它们加起来对应于您测量的 13 个周期。

您可能会注意到,在某些周期中,只有少量指令被预解码;例如,在第四个周期,只有一条指令被预解码。这是因为预解码器在对齐的 16 字节块上工作,它每个周期最多可以处理 5 条指令(请注意,一些消息来源错误地声称它每个周期可以处理 6 条指令)。您可以在this paper 中找到有关预解码器的更多详细信息,例如它如何处理跨越 16 字节边界的指令。

如果将此迹线与 g1b 的迹线进行比较, 可以看到,nop 之后的指令现在需要 6 个而不是 5 个周期进行预解码,这是因为 g1b 中的一些指令比 g1a 中的相应指令长。

【讨论】:

  • 很好的解释和酷酷的模拟器!在您链接的结果中,g2a 和 g2b 实际上选择了不同的端口。你是怎么模拟的?(我还没有读过你的论文,也许以后再说吧。)
  • 我已经阅读了你的论文 2.12。这可以解释为什么dec edi 去端口 1 而dec rdi 去端口 0?
  • @moep0 是的,dec edi 使用问题槽 0,而 dec rdi 使用问题槽 1,这说明了不同的端口使用情况。我不确定 g2a 和 g2b,我需要调查一下。
猜你喜欢
  • 1970-01-01
  • 2011-10-26
  • 1970-01-01
  • 2017-05-12
  • 2016-01-18
  • 2018-08-28
  • 2017-06-05
  • 2014-02-21
  • 2010-09-15
相关资源
最近更新 更多