performance - 在 x86 汇编中,对 imul 使用两个单独的寄存器是否更好?

标签 performance assembly x86 micro-optimization

我想知道,主要是出于好奇,对于一个操作使用同一个寄存器是否比使用两个更好。考虑到性能和/或其他问题,什么会更好?

mov %rbx, %rcx
imul %rcx, %rcx

或者
mov %rbx, %rcx
imul %rbx, %rcx

任何有关如何对此进行基准测试的提示,或者我可以阅读有关此类事情的资源,都将不胜感激,因为我是组装新手。

最佳答案

resources where I could read about this type of thing



Agner Fog's microarch pdf ,以及他的优化 assembly 指南。还有其他链接在标签 wiki(例如 Intel 的优化手册)。

您没有提到的有趣选项是:
mov   %rbx, %rcx
imul  %rbx, %rbx     # doesn'y have to wait for mov to execute
# old value of %rbx is still available in %rcx

如果imul处于关键路径上,并且 mov具有非零延迟(如 AMD CPU 和 IvyBridge 之前的 Intel),这可能更好。 imul的结果将提前一个周期准备好,因为不依赖于 mov 的结果.

但是,如果旧值在关键路径上而平方值不在关键路径上,则情况更糟,因为它添加了 mov到关键路径。

当然,这也意味着您必须跟踪这样一个事实,即您的旧变量现在位于不同的寄存器中,并且旧寄存器具有平方值。如果这是循环中的问题,请展开它,这样您就可以得到循环顶部所期望的结果。如果您希望这很容易,您可以使用编译器而不是手动优化 asm。

但是,Intel P6 系列 CPU(PPro/PII 到 Nehalem)具有 有限的寄存器读取端口 ,因此最好选择读取您刚刚编写的寄存器。如果%rbx没有在最后几个周期写入,当 mov 时,必须从永久寄存器文件中读取它。和 imul uops 经历重命名和发布阶段(RAT)。

如果他们不作为同一组 4 人的一部分发行,那么他们每个人都需要阅读 %rbx分别地。由于 Core2/Nehalem 中的寄存器文件只有 3 个读取端口,问题组(四重奏,如 Agner Fog 所称的那样)会停止,直到从寄存器文件中读取所有最近未写入的输入寄存器值(每个周期 3 个,或Core2 上的 2 不是 3 个寄存器中的任何一个是寻址模式下的索引寄存器)。

有关完整的详细信息,请参阅 Agner Fog's microarch pdf第 8.8 节。 Core2 部分指回 PPro 部分。 PPro 有一个 3 宽的管道,所以在该部分 Agner 谈论的是三重奏,而不是四重奏。

movimul一起发布,他们都读了 %rbx .在 Core2/Nehalem 上发生这种情况的几率为四分之三。

对于 Intel P6 系列 CPU,仅在您提到的第一个序列之间进行选择比第二个具有明显(但通常很小)的优势。其他 CPU 没有区别,AFAIK,所以选择是显而易见的。
mov   %rbx, %rcx
imul  %rcx, %rcx     # uses only the recently-written rcx; can't contribute to register-read stalls

两全其美:
mov   %rbx, %rcx
imul  %rbx, %rcx     # can't execute until after the mov, but still reads a potentially-old register

如果您要依赖最近写入的寄存器,则最好只使用最近写入的寄存器。

Intel Sandybridge 系列使用物理寄存器文件(如 AMD Bulldozer 系列),并且没有寄存器读取停顿。

Ivybridge(第二代 Sandybridge)和更高版本也处理 mov reg,reg在寄存器重命名时,零延迟且没有执行单元。这意味着您是否模仿都无关紧要 rbxrcx至于关键路径长度。

但是,AMD Bulldozer 系列只能在重命名阶段处理 xmm 寄存器移动;整数寄存器移动仍然有 1c 延迟。

可能仍然值得关注 mov 哪个依赖链如果延迟是循环中每次迭代的周期的限制因素,则是其中的一部分。

how to benchmark this



我认为您可以使用 imul %rbx, %rcx 组合一个在 Core2 上具有寄存器读取停顿的微基准测试。 ,但不是 imul %rcx, %rcx .但是,这需要反复试验才能获得 movimul在不同的组中发布,除非您感觉非常有创意,否则可能会出现一些看起来很假的周围代码,这些代码仅存在于读取大量寄存器中。 (例如 lea (%rsi, %rdi, 1), %eax ,甚至 add (%rsi, %rdi, 1), %eax (它必须读取所有三个寄存器,并在 core2/nehalem 上进行微保险,因此它在问题组中只需要 1 个 uop 插槽。(它 doesn't micro-fuse on SnB-family ))。

关于performance - 在 x86 汇编中,对 imul 使用两个单独的寄存器是否更好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37915560/

相关文章:

macos - 具有嵌套相对宽度的自动布局性能?

python - 当条件为两个时有效地更新一个 numpy 数组

assembly - MIPS 标签寻址?

c++ - FPU,SSE 单 float 。哪个更快?低于或高于

assembly - 获得汇编语言编程技能

assembly - 如何取反存储在 32 位寄存器对中的 64 位整数?

javascript - 是什么导致相同数量的循环具有不同的性能?

javascript - AngularJS 异常高的内存消耗

c++ - 了解 FMA 性能

c++ - std::atomic 的锁在哪里?