gpt4 book ai didi

assembly - Haswell/Skylake 上的部分寄存器究竟如何执行?写AL好像对RAX有假依赖,AH不一致

转载 作者:行者123 更新时间:2023-12-03 09:11:48 30 4
gpt4 key购买 nike

这个循环在 Intel Conroe/Merom 上每 3 个周期运行一次迭代,如预期的那样在 imul 吞吐量上出现瓶颈。但是在 Haswell/Skylake 上,它每 11 个周期运行一次迭代,显然是因为 setnz al 依赖于最后一个 imul

; synthetic micro-benchmark to test partial-register renaming
mov ecx, 1000000000
.loop: ; do{
imul eax, eax ; a dep chain with high latency but also high throughput
imul eax, eax
imul eax, eax

dec ecx ; set ZF, independent of old ZF. (Use sub ecx,1 on Silvermont/KNL or P4)
setnz al ; ****** Does this depend on RAX as well as ZF?
movzx eax, al
jnz .loop ; }while(ecx);

如果 setnz al 依赖于 rax ,则 3ximul/setcc/movzx 序列形成循环携带的依赖链。如果不是,则每个 setcc/ movzx/3x imul 链都是独立的,从更新循环计数器的 dec fork 出来。在 HSW/SKL 上测得的每次迭代 11c 可以用延迟瓶颈完美解释:3x3c(imul) + 1c(setcc 读取-修改-写入)+ 1c(同一寄存器内的 movzx)。

题外话:避免这些(有意的)瓶颈

我想要可理解/可预测的行为来隔离部分注册的东西,而不是最佳性能。

例如, xor -zero/set-flags/ setcc 无论如何都更好(在这种情况下, xor eax,eax/ dec ecx/10x23131)。这打破了所有 CPU 上的 eax 依赖(除了早期的 P6 系列,如 PII 和 PIII),仍然避免了部分寄存器合并惩罚,并节省了 1c 的 setnz al 延迟。它还在 handle xor-zeroing in the register-rename stage 的 CPU 上使用少一个 ALU uop。有关使用 movzx 的异或归零的更多信息,请参阅该链接。

请注意,AMD、Intel Silvermont/KNL 和 P4 根本不进行部分寄存器重命名。它只是英特尔 P6 系列 CPU 及其后代英特尔 Sandybridge 系列 CPU 中的一项功能,但似乎正在逐步淘汰。

GCC遗憾的是并倾向于使用 setcc/ cmp/ setcc al它也可以使用 movzx eax,al代替 xor (Godbolt compiler-explorer example),而铛使用XOR零/CMP/setcc除非您将多个 bool 条件像 movzx

xor/dec/setnz 版本在 Skylake、Haswell 和 Core2 上以每次迭代 3.0c 的速度运行(瓶颈为 count += (a==b) | (a==~b) 吞吐量)。 imul -zeroing 在除 PPro/PII/PIII/early-Pentium-M 之外的所有乱序 CPU 上打破了对旧值 xor 的依赖(它仍然避免部分寄存器合并惩罚,但不会破坏 dep )。 Agner Fog's microarch guide describes this。用 eax 替换异或归零会使其在 Core2 上每 4.78 个周期减慢到一个: 2-3c stall (in the front-end?) to insert a partial-reg merging uopmov eax,0 读取 0x251812231143 1343 之后的 0x2518122311343 1343 之后。

另外,我使用了 imul 来打败 mov-elimination,就像 eax 一样。 (IvB、HSW 和 SKL 可以以 0 延迟重命名 setnz al,但 Core2 不能)。这使得 Core2/SKL 中的所有内容都相等,除了部分寄存器行为。

Core2 行为与 Agner Fog's microarch guide 一致,但 HSW/SKL 行为不一致。来自 Skylake 的第 11.10 节,与之前的 Intel uarches 相同:

Different parts of a general purpose register can be stored in different temporary registers in order to remove false dependences.



不幸的是,他没有时间对每个新的 uarch 进行详细测试以重新测试假设,因此这种行为上的变化从裂缝中溜走了。

Agner 确实描述了通过 Skylake 在 Sandybridge 上为 high8 寄存器 (AH/BH/CH/DH) 以及在 SnB 上为 low8/low16 插入(没有停顿)的合并 uop。 (不幸的是我过去一直在散布错误信息,并说 Haswell 可以免费合并 AH。我浏览了 Agner 的 Haswell 部分太快了,没有注意到后面关于 high8 寄存器的段落。如果你看到请告诉我我对其他帖子的错误评论,所以我可以删除它们或添加更正。我会尝试至少在我说过的地方找到并编辑我的答案。)

我的实际问题: 部分寄存器在 Skylake 上究竟表现如何?

从 IvyBridge 到 Skylake,一切都一样吗,包括 high8 额外延迟?

Intel's optimization manual 没有具体说明哪些 CPU 对什么具有错误的依赖关系(尽管它确实提到某些 CPU 具有它们),并且省略了诸如读取 AH/BH/CH/DH(高 8 寄存器)之类的事情,即使它们没有增加额外的延迟被修改。

如果 Agner Fog 的微架构指南没有描述任何 P6 系列(Core2/Nehalem)行为,那也会很有趣,但我可能应该将这个问题的范围限制在 Skylake 或 Sandybridge 系列。

我的 Skylake 测试数据 ,来自将 movzx eax, al 短序列放入运行 100M 或 1G 迭代的小 mov rax,rax 循环中。我使用 Linux movzx eax, bl 以与 in my answer here 相同的方式在相同的硬件(桌面 Skylake i7 6700k)上测量了周期。

除非另有说明,每条指令都作为 1 个融合域 uop 运行,使用 ALU 执行端口。 (用 %rep 4 测量)。这会检测(不存在)移动消除和额外的合并 uops。

“每个周期 4”情况是对无限展开情况的外推。循环开销占用了一些前端带宽,但每个周期优于 1 的任何情况都表明寄存器重命名避免了 write-after-write output dependency ,并且 uop 不在内部作为读-修改-写处理。

仅写入 AH :防止循环从环回缓冲区(又名循环流检测器 (LSD))执行。 dec ebp/jnz 的计数在 HSW 上正好是 0,在 SKL 上很小(大约 1.8k),并且不随循环迭代计数而扩展。可能这些计数来自某些内核代码。当循环从 LSD 运行时, perf 在测量噪声范围内。有些循环在 LSD 或无 LSD 之间交替(例如,如果解码在错误的位置开始,它们可能不适合 uop 缓存),但我在测试时没有遇到这种情况。
  • 重复 ocperf.py stat -e ...,uops_issued.any,uops_executed.thread 和/或 lsd.uops 每个周期运行 4。它需要一个 ALU uop,所以它不会像 lsd.uops ~= uops_issued 那样被消除。
  • 重复 mov ah, bh 每周期运行 2 个(负载吞吐量瓶颈)。
  • 重复 mov ah, bl 每个周期运行 1。 (循环内的 dep-breaking mov eax, ebx 消除了瓶颈。)
  • 重复 mov ah, [rsi]mov ah, 123 每个周期运行 1。 (一个破坏性的 xor eax,eax 让它成为 setz ah 和循环分支的 p06 吞吐量瓶颈。)

    为什么用通常会使用 ALU 执行单元的指令写入 setc ah 对旧值有错误的依赖性,而 xor eax,eax 则没有(对于 reg 或内存 src)? (那么 setcc 呢?当然,您用于 reg-reg 移动的两个操作码中的哪一个都无关紧要?)
  • 正如预期的那样,
  • 重复 ah 每个周期运行 1。
  • 重复 mov r8, r/m8 每个周期运行 1。
  • 重复 mov r/m8, r8 每个周期运行 1。
  • 重复 add ah, 123 每周期运行 0.5。读取 [ABCD]H 当它们“干净”时很特别(在这种情况下,RCX 最近根本没有修改)。

  • 术语 :所有这些都会留下 AH(或 DH)“ ”,即在其他情况下需要读取寄存器时(即其他寄存器的读取)。即 AH 与 RAX 分开重命名,如果我理解正确的话。 “ clean ”则相反。有很多方法可以清除脏寄存器,最简单的是 add dh, cladd dh, dh

    仅写入 AL :这些循环确实从 LSD 运行: add dh, ch ~= inc eax
  • 重复 mov eax, esi 每个周期运行 1。每组偶尔的 dep-breaking uops_issue.any 让 OOO 执行成为 uop 吞吐量的瓶颈,而不是延迟。
  • 重复 lsd.uops 每个周期运行 1 个,作为微融合 ALU+负载 uop。 (uops_issued=4G + 循环开销,uops_executed=8G + 循环开销)。
    在一组 4 之前,一个破坏性的 mov al, bl 让它成为每个时钟 2 个负载的瓶颈。
  • 重复 xor eax,eax 每个周期运行 1。
  • 重复 mov al, [rsi] 每周期运行 0.5。 (每 2 个周期 1 个)。读 [ABCD]H 很特别。
  • xor eax,eax + 6x mov al, 123 + mov al, bh:每个迭代 2c,每个时钟 4 uops 的瓶颈。
  • 重复 xor eax,eax 每周期运行 0.5。 (每 2 个周期 1 个)。读取 [ABCD]H 显然会为 mov al,bh 造成额外的延迟。
  • 重复 dec ebp/jnz 每个周期运行 1。

  • 我认为写入低 8 reg 的行为就像 RMW 混合到完整 reg 中,就像 add dl, ch 一样,但如果 dl 是脏的,它不会触发合并。因此(除了忽略 add dl, cl 合并),它的行为与根本不进行部分 reg 重命名的 CPU 相同。似乎 add eax, 123 从未与 ah 分开重命名?
  • AH/AL 对可以并行运行。
  • RAX 如果 inc al 是“脏的”,则插入合并 uop,但实际的 inc ah 已重命名。这是 IvyBridge 及更高版本的 Agner Fog describes
  • 重复 mov ecx, eax 每 2 个周期运行 1 个。 (在写入完整寄存器后读取高 8 寄存器有额外的延迟。)
  • ah 具有零延迟并且不占用 HSW 和 SKL 上的执行端口。 (就像 Agner Fog 为 IvyBridge 描述的那样,但他说 HSW 不会重命名 movzx)。
  • mov 有 1c 延迟并占用一个执行端口。 ( mov-elimination never works for the movzx eax, ah case ,仅在不同架构寄存器之间。)

    每次迭代都插入合并 uop 的循环不能从 LSD(循环缓冲区)运行?

  • 我不认为 AL/AH/RAX 与 B*、C*、DL/DH/RDX 有什么特别之处。我已经在其他寄存器中使用部分 regs 测试了一些(即使我主要显示 movzx ecx, al/ movzx ecx, cl 以保持一致性),并且从未注意到任何差异。

    我们如何用一个关于微拱内部如何工作的合理模型来解释所有这些观察结果?

    相关:部分 标志 问题不同于部分 寄存器 0x​​251819211341142 问题。有关 same,same 的一些 super 奇怪的内容,请参阅 INC instruction vs ADD 1: Does it matter?(甚至 Core2/Nehalem 上的 AL:不要从除 1 以外的移位读取标志)。

    另请参阅 Problems with ADC/SBB and INC/DEC in tight loops on some CPUs 以了解 AH 循环中的部分标志内容。

    最佳答案

    其他答案欢迎更详细地解决 Sandybridge 和 IvyBridge。
    我无权访问该硬件。

    我没有发现 HSW 和 SKL 之间的任何部分 reg 行为差异。
    在 Haswell 和 Skylake 上,到目前为止我测试过的所有东西都支持这个模型:

    AL 从未与 RAX (或来自 r15 的 r15b)分开重命名。因此,如果您从未接触过 high8 寄存器(AH/BH/CH/DH),那么一切都与没有部分 reg 重命名(例如 AMD)的 CPU 完全一样。

    对 AL 的只写访问合并到 RAX,依赖于 RAX。对于加载到 AL 中的内容,这是一个在 p0156 上执行的微融合 ALU+加载 uop,这是最有力的证据之一,表明它在每次写入时真正合并,而不仅仅是像 Agner 推测的那样进行一些花哨的双重簿记。

    Agner(和英特尔)说 Sandybridge 可能需要为 AL 合并 uop,因此它可能与 RAX 分开重命名。对于 SnB,Intel's optimization manual (section 3.5.2.4 Partial Register Stalls)

    SnB (not necessarily later uarches) inserts a merging uop in the following cases:

    • After a write to one of the registers AH, BH, CH or DH and before a following read of the 2-, 4- or 8-byte form of the same register. In these cases a merge micro-op is inserted. The insertion consumes a full allocation cycle in which other micro-ops cannot be allocated.

    • After a micro-op with a destination register of 1 or 2 bytes, which is not a source of the instruction (or the register's bigger form), and before a following read of a 2-,4- or 8-byte form of the same register. In these cases the merge micro-op is part of the flow.



    我认为他们是说在 SnB 上, add al,bl 将 RMW 完整的 RAX 而不是单独重命名它,因为源寄存器之一是(部分)RAX。我的猜测是这不适用于像 mov al, [rbx + rax] 这样的负载; rax 在寻址模式下可能不算作来源。

    我还没有测试过 high8 合并 uops 是否仍然需要在 HSW/SKL 上自行发布/重命名。这将使前端影响相当于 4 uop(因为这是问题/重命名管道宽度)。
  • 没有办法在不写EAX/RAX的情况下打破涉及AL的依赖。 xor al,al 没有帮助, mov al, 0 也没有帮助。
  • movzx ebx, alzero latency (renamed) ,不需要执行单元。 (即移动消除适用于 HSW 和 SKL)。 如果它是脏的,它会触发 AH 的合并 ,我想这是它在没有 ALU 的情况下工作所必需的。英特尔在引入 mov-elimination 的同一个 uarch 中放弃了 low8 重命名可能并非巧合。 (Agner Fog的微弓指南这里有一个错误,说在HSW或SKL上没有消除零扩展 Action ,只有IvB。)
  • movzx eax, al 在重命名时不会被消除。英特尔上的 mov-elimination 永远不会同样适用。 mov rax,rax 也没有被消除,即使它不必对任何东西进行零扩展。 (尽管没有必要为其提供特殊的硬件支持,因为它只是一个空操作,与 mov eax,eax 不同)。无论如何,在零扩展时更喜欢在两个单独的架构寄存器之间移动,无论是使用 32 位 mov 还是 8 位 movzx
  • movzx eax, bx 在 HSW 或 SKL 上重命名时不会被消除。它具有 1c 延迟并使用 ALU uop。 Intel 的优化手册仅提到 8 位 movzx 的零延迟(并指出 movzx r32, high8 从未重命名)。


  • High-8 regs 可以与寄存器的其余部分分开重命名,并且确实需要合并 uops。
  • 只写访问 ahmov ah, reg8mov ah, [mem8] 重命名 AH,不依赖旧值。这些都是 32 位版本通常不需要 ALU uop 的指令。 (但是 mov ah, bl 没有被消除;它确实需要一个 p0156 ALU uop,所以这可能是一个巧合)。
  • AH的RMW(如 inc ah )弄脏了它。
  • setcc ah 依赖于旧的 ah ,但仍然弄脏它。我认为 mov ah, imm8 是相同的,但还没有测试那么多的极端情况。

    (无法解释:一个涉及 setcc ah 的循环有时可以从 LSD 运行,参见本文末尾的 0x25181231343141 循环。也许只要 rcr 在循环结束时是干净的,它就可以使用?)。

    如果 ah 是脏的, ah 合并到重命名的 setcc ah ,而不是强制合并到 ah 。例如rax(%rep 4/inc al/test ebx,ebx/setcc ah/inc al)不产生合并微指令,只有在大约8.7c运行(等待时间的8 inc ah从微指令减慢由资源冲突为inc al。另外,ah/inc ah DEP链)。

    我认为这里发生的事情是 setcc ah 始终实现为读-修改-写。英特尔可能认为不值得使用只写 setcc r8 uop 来优化 setcc 情况,因为编译器生成的代码非常罕见 setcc ah 。 (但请参阅问题中的 Godbolt 链接:clang4.0 with setcc ah 会这样做。)
  • 读取 AX、EAX 或 RAX 会触发合并 uop(占用前端问题/重命名带宽)。 RAT(寄存器分配表)可能会跟踪架构 R[ABCD]X 的高 8 脏状态,即使在对 AH 的写入退出后,AH 数据也存储在与 RAX 不同的物理寄存器中。即使在写入 AH 和读取 EAX 之间有 256 个 NOP,还有一个额外的合并 uop。 (SKL 上的 ROB 大小=224,因此这保证了 -m32 已停用)。使用 uops_issued/executed perf 计数器检测到,这清楚地显示了差异。
  • AL 的读-修改-写(例如 mov ah, 123 )作为 ALU uop 的一部分免费合并。 (仅使用一些简单的 uops 进行测试,例如 inc al/add ,而不是 incdiv r8 )。同样,即使 AH 是脏的,也不会触发合并 uop。
  • 只写到 EAX/RAX(如 mul r8 lea eax, [rsi + rcx] )清除 AH 脏状态(无合并 uop)。
  • 只写到 AX ( xor eax,eax ) 首先触发 AH 的合并。我想它不是特殊 shell ,而是像 AX/RAX 的任何其他 RMW 一样运行。 (待办事项:测试 mov ax, 1 ,虽然这不应该很特别,因为它没有重命名。)
  • mov ax, bx 有 1c 延迟,不破坏,仍然需要一个执行端口。
  • AL 的读取和/或写入不会强制合并,因此 AH 可以保持脏状态(并在单独的 dep 链中独立使用)。 (例如 xor ah,ah/add ah, cl 每个时钟可以运行 1 个(瓶颈增加延迟)。


  • 使 AH 变脏可防止循环从 LSD (循环缓冲区)运行,即使没有合并 uops。 LSD 是当 CPU 在提供问题/重命名阶段的队列中回收 uops 时。 (称为 IDQ)。

    插入合并 uops 有点像为堆栈引擎插入堆栈同步 uops。 Intel 的优化手册说 SnB 的 LSD 不能运行具有不匹配 add al, dl/ push 的循环,这是有道理的,但这意味着它可以运行具有平衡 0x251812231340x1221313/.40x12413 的循环这不是我所看到的SKL:即使平衡从LSD运行(如 pop/ push/ pop(有可能是瑞士央行的LSD和HSW/SKL之间的真正区别 push/ pop防止:。 SnB may just "lock down" the uops in the IDQ instead of repeating them multiple times, so a 5-uop loop takes 2 cycles to issue instead of 1.25)无论如何,它出现当高 8 寄存器脏时,或者当它包含堆栈引擎 uops 时,HSW/SKL 不能使用 LSD。

    此行为可能与 an erratum in SKL 相关:

    SKL150: Short Loops Which Use AH/BH/CH/DH Registers May Cause Unpredictable System Behaviour

    Problem: Under complex micro-architectural conditions, short loops of less than 64 instruction that use AH, BH, CH, or DH registers as well as their corresponding wider registers (e.g. RAX, EAX, or AX for AH) may cause unpredictable system behaviour. This can only happen when both logical processors on the same physical processor are active.



    这可能也与 Intel 的优化手册声明有关,即 SnB 至少必须自己在一个周期内发出/重命名 AH-merge uop。对于前端来说,这是一个奇怪的差异。

    我的 Linux 内核日志显示 push rax
    Arch Linux 的 pop rdx 软件包仅提供更新 you have to edit config files to actually have it loaded 。所以 我的 Skylake 测试是在 i7-6700k 上进行的,微码修订版为 0x84,即 doesn't include the fix for SKL150 。它与我测试的每种情况下的 Haswell 行为相匹配,IIRC。 (例如,Haswell 和我的 SKL 都可以从 LSD 运行 times 6 imul rax, rdx/ microcode: sig=0x506e3, pf=0x2, revision=0x84/ intel-ucode/ setne ah 循环)。我启用了 HT(这是 SKL150 出现的先决条件),但我在一个大部分空闲的系统上进行测试,因此我的线程拥有自己的核心。

    使用更新的微码,LSD 始终对所有内容完全禁用,而不仅仅是在部分寄存器处于事件状态时。 add ah,ah 总是恰好为零,包括真实程序而不是合成循环。硬件错误(而不是微代码错误)通常需要禁用整个功能才能修复。这就是 SKL-avx512 (SKX) 为 reported to not have a loopback buffer 的原因。幸运的是,这不是性能问题:SKL 在 Broadwell 上增加的 uop-cache 吞吐量几乎总是能跟上问题/重命名。

    额外的 AH/BH/CH/DH 延迟:
  • 在不脏时读取 AH(单独重命名)为两个操作数增加了额外的延迟周期。例如rcr ebx,1 从输入 BL 到输出 BL 的延迟为 2c,因此即使 RAX 和 AH 不是关键路径的一部分,它也可以为关键路径增加延迟。 (我以前见过其他操作数的这种额外延迟,在 Skylake 上有向量延迟,其中 int/float 延迟永远“污染”寄存器。TODO:写下来。)

  • 这意味着使用 mov eax,ebx/ lsd.uops 解包字节具有额外的延迟,而 add bl, ah/ movzx ecx, al/0x2518141/0x2518141 3 吞吐量仍然更好,3
  • 脏时读取 AH 不会增加任何延迟。 ( movzx edx, ahmovzx/shr eax,8 每次添加有 1c 延迟)。在许多极端情况下,我还没有进行大量测试来确认这一点。

    假设:一个脏的 high8 值存储在物理寄存器 0x​​251819211341142 的底部。读取一个干净的 high8 需要移位以提取位 [15:8],但读取一个脏的 high8 可以像正常的 8 位寄存器读取一样读取物理寄存器的位 [7:0]。

  • 额外的延迟并不意味着吞吐量降低。即使所有 movzx 指令都有 2c 延迟(从读取未修改的 DH 开始),该程序也可以每 2 个时钟运行 1 个迭代。
    global _start
    _start:
    mov ebp, 100000000
    .loop:
    add ah, dh
    add bh, dh
    add ch, dh
    add al, dh
    add bl, dh
    add cl, dh
    add dl, dh

    dec ebp
    jnz .loop

    xor edi,edi
    mov eax,231 ; __NR_exit_group from /usr/include/asm/unistd_64.h
    syscall ; sys_exit_group(0)
     Performance counter stats for './testloop':

    48.943652 task-clock (msec) # 0.997 CPUs utilized
    1 context-switches # 0.020 K/sec
    0 cpu-migrations # 0.000 K/sec
    3 page-faults # 0.061 K/sec
    200,314,806 cycles # 4.093 GHz
    100,024,930 branches # 2043.675 M/sec
    900,136,527 instructions # 4.49 insn per cycle
    800,219,617 uops_issued_any # 16349.814 M/sec
    800,219,014 uops_executed_thread # 16349.802 M/sec
    1,903 lsd_uops # 0.039 M/sec

    0.049107358 seconds time elapsed

    一些有趣的测试循环体 :
    %if 1
    imul eax,eax
    mov dh, al
    inc dh
    inc dh
    inc dh
    ; add al, dl
    mov cl,dl
    movzx eax,cl
    %endif

    Runs at ~2.35c per iteration on both HSW and SKL. reading `dl` has no dep on the `inc dh` result. But using `movzx eax, dl` instead of `mov cl,dl` / `movzx eax,cl` causes a partial-register merge, and creates a loop-carried dep chain. (8c per iteration).


    %if 1
    imul eax, eax
    imul eax, eax
    imul eax, eax
    imul eax, eax
    imul eax, eax ; off the critical path unless there's a false dep

    %if 1
    test ebx, ebx ; independent of the imul results
    ;mov ah, 123 ; dependent on RAX
    ;mov eax,0 ; breaks the RAX dependency
    setz ah ; dependent on RAX
    %else
    mov ah, bl ; dep-breaking
    %endif

    add ah, ah
    ;; ;inc eax
    ; sbb eax,eax

    rcr ebx, 1 ; dep on add ah,ah via CF
    mov eax,ebx ; clear AH-dirty

    ;; mov [rdi], ah
    ;; movzx eax, byte [rdi] ; clear AH-dirty, and remove dep on old value of RAX
    ;; add ebx, eax ; make the dep chain through AH loop-carried
    %endif

    setcc 版本(带有 add ah,ah )具有 20c 循环携带延迟,并且从 LSD 运行,即使它具有 add ah,dhadd dh,ah
    00000000004000e0 <_start.loop>:
    4000e0: 0f af c0 imul eax,eax
    4000e3: 0f af c0 imul eax,eax
    4000e6: 0f af c0 imul eax,eax
    4000e9: 0f af c0 imul eax,eax
    4000ec: 0f af c0 imul eax,eax
    4000ef: 85 db test ebx,ebx
    4000f1: 0f 94 d4 sete ah
    4000f4: 00 e4 add ah,ah
    4000f6: d1 db rcr ebx,1
    4000f8: 89 d8 mov eax,ebx
    4000fa: ff cd dec ebp
    4000fc: 75 e2 jne 4000e0 <_start.loop>

    Performance counter stats for './testloop' (4 runs):

    4565.851575 task-clock (msec) # 1.000 CPUs utilized ( +- 0.08% )
    4 context-switches # 0.001 K/sec ( +- 5.88% )
    0 cpu-migrations # 0.000 K/sec
    3 page-faults # 0.001 K/sec
    20,007,739,240 cycles # 4.382 GHz ( +- 0.00% )
    1,001,181,788 branches # 219.276 M/sec ( +- 0.00% )
    12,006,455,028 instructions # 0.60 insn per cycle ( +- 0.00% )
    13,009,415,501 uops_issued_any # 2849.286 M/sec ( +- 0.00% )
    12,009,592,328 uops_executed_thread # 2630.307 M/sec ( +- 0.00% )
    13,055,852,774 lsd_uops # 2859.456 M/sec ( +- 0.29% )

    4.565914158 seconds time elapsed ( +- 0.08% )

    无法解释:它从 LSD 运行,即使它使 AH 变脏。 (至少我认为它确实如此。TODO:尝试添加一些指令,在 add 清除它之前对 %if 1 执行某些操作。)

    但是对于 setcc ah ,它在 HSW/SKL 上每次迭代都以 5.0c 运行(add ah,ah 吞吐量瓶颈)。 (注释掉的存储/重新加载也可以工作,但是 SKL 的存储转发速度比 HSW 快,并且是 variable-latency ...)
     #  mov ah, bl   version
    5,009,785,393 cycles # 4.289 GHz ( +- 0.08% )
    1,000,315,930 branches # 856.373 M/sec ( +- 0.00% )
    11,001,728,338 instructions # 2.20 insn per cycle ( +- 0.00% )
    12,003,003,708 uops_issued_any # 10275.807 M/sec ( +- 0.00% )
    11,002,974,066 uops_executed_thread # 9419.678 M/sec ( +- 0.00% )
    1,806 lsd_uops # 0.002 M/sec ( +- 3.88% )

    1.168238322 seconds time elapsed ( +- 0.33% )

    请注意,它不再从 LSD 运行。

    关于assembly - Haswell/Skylake 上的部分寄存器究竟如何执行?写AL好像对RAX有假依赖,AH不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45660139/

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