繁体   English   中英

在 64 位 x64/Amd64 处理器上执行 8 位和 64 位指令的时序

[英]Timing of executing 8 bit and 64 bit instructions on 64 bit x64/Amd64 processors

8 it 和 64 位指令在 64 位 x64/Amd64 处理器上是否有任何执行时序差异,当这些指令除了位宽之外是相似/相同时? 有没有办法找到执行这两个微型汇编函数的真实处理器时序?

-谢谢。

; 64 bit instructions
add64:
     mov  $0x1, %rax
     add  $0x2, %rax
     ret

; 8 bit instructions
add8:
     mov  $0x1, %al
     add  $0x2, %al
     ret

是的,有区别。 mov $0x1, %al在大多数 CPU 上对 RAX 的旧值有错误的依赖,包括比 Sandybridge 更新的所有 CPU。 这是一个2输入1输出指令; 从 CPU 的角度来看,它就像add $1, %al一样独立或不相对于 RAX 的其他用途进行调度。 仅写入 32 位或 64 位寄存器会启动新的依赖链。

这意味着您的add8 function 的 AL 返回值可能无法准备好,直到调用者在调用之前恰好在 EAX 中执行的某些独立工作的缓存未命中后才准备好,但add64的 RAX 结果可能会立即准备好用于 out-of -order 执行以在调用者中使用返回值的后续指令上开始。 (假设他们的其他输入也准备好了。)

它们的代码大小也不同:两条 8 位指令都是 2 个字节长。 (感谢 AL,imm8 短格式编码; add $1, %dl将是 3 个字节)。 RAX 指令有 7 个和 4 个字节长。 这对于 L1i 缓存占用很重要(在大规模上,对于必须从磁盘调入多少字节)。 在小范围内,如果 CPU 正在执行旧版解码,那么 16 或 32 字节的提取块可以容纳多少条指令,因为代码在 uop 缓存中还不是很热。 后面指令的代码对齐也会受到前面指令的不同长度的影响,有时会影响哪些分支相互别名。

https://agner.org/optimize/解释了各种 x86 微架构的流水线细节,包括前端解码效果,可以使指令长度不仅仅是 I-cache / uop-cache 中的代码密度。

通常 32 位操作数大小是最有效的(就性能而言,并且对于代码大小来说非常好) 32 和 8 是 x86-64 可以在没有额外前缀的情况下使用的操作数大小,实际上使用 8 位来避免停顿和坏事,您需要更多指令或更长的指令,因为它们不会零扩展。 在 x86-64 中使用 32 位寄存器/指令的优点

对于 64 位操作数大小,ALU 中的一些指令实际上更慢,而不仅仅是前端效果。 这包括大多数 CPU 上的div和一些旧 CPU 上的imul 还有 popcnt 和 bswap。 例如,试除法代码在 Windows 上运行 32 位比在 Linux 上运行 64 位快 2 倍

请注意, mov $0x1, %rax将使用 GAS 组装成 7 个字节,除非您使用as -O2 (与gcc -O2不同,请参见示例)以使其优化为mov $1, %eax ,这正是相同的架构效果,但更短(没有 REX 或 ModRM 字节)。 一些汇编程序默认情况下会进行优化,但 GAS 不会。 为什么 Linux 上的 NASM 更改 x86_64 程序集中的寄存器有更多关于为什么这种优化是安全和良好的,以及为什么你应该在源代码中自己做,特别是如果你的汇编程序不为你做。


但是除了错误的 dep 和代码大小之外,它们对于 CPU 的后端是相同的:所有这些指令都是单 uop 并且可以在任何标量整数 ALU 执行端口1上运行。 https://uops.info/对每条非特权指令的每种形式都有自动测试结果)。

脚注 1 :挖掘机(上一代 Bulldozer 系列)还可以在另外 2 个端口 (AGU) 上运行mov $imm, %reg ,用于 32 位和 64 位操作数大小。 但是将新的低 8 或低 16 合并到一个完整的寄存器中需要一个 ALU 端口。 所以mov $1, %rax在 Excavator 上有 4/clock 的吞吐量,但是mov $1, %al只有 2/clock 的吞吐量。 (当然,仅当您使用几个不同的目标寄存器时,实际上不是重复使用 AL;这将是 1/clock 的延迟瓶颈,因为在该微架构上写入部分寄存器会产生错误的依赖性。)

以前从 Piledriver 开始的 Bulldozer 系列 CPU 可以在 EX0、EX1、AGU0、AGU1 上运行mov reg, reg (用于 r32 或 r64),而包括mov $imm, %reg在内的大多数 ALU 指令只能在 EX0/1 上运行。 进一步扩展 AGU 端口的功能以处理 mov-immediate 是 Excavator 中的一项新功能。

幸运的是,推土机被 AMD 更好的 Zen 架构淘汰了,该架构具有 4 个全标量 integer ALU 端口/执行单元。 (还有一个更宽的前端和一个 uop 缓存,良好的缓存,并且通常不会像 Bulldozer 那样糟糕。)


有没有办法测量它?

是的,但通常不在您使用call的 function 中。 而是把它放在一个展开的循环中,这样你就可以用最少的其他指令运行它很多次。 查看 CPU 性能计数器结果以查找前端/后端 uop 计数以及循环的总时间特别有用。

您可以构建循环来测量延迟或吞吐量; 请参阅NASM 中的 RDTSCP 始终返回相同的值(计时单个指令) 还:

通常,您不需要测量自己(尽管了解如何测量很好,这有助于您了解测量的真正含义)。 人们已经为大多数 CPU 微架构做到了这一点。 您可以基于分析指令来预测特定 CPU 的某些循环的性能(如果您可以假设没有停顿或缓存未命中)。 通常这可以相当准确地预测性能,但是 OoO exec 只能部分隐藏的中等长度的依赖链使得准确预测或解释每个周期变得过于困难。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM