13

在汇编指令级别分析代码时,鉴于现代 CPU 不串行或按顺序执行指令,指令指针的位置真正意味着什么?例如,假设以下 x64 汇编代码:

mov RAX, [RBX];         // Assume a cache miss here.
mov RSI, [RBX + RCX];   // Another cache miss.             
xor R8, R8;        
add RDX, RAX;           // Dependent on the load into RAX.
add RDI, RSI;           // Dependent on the load into RSI.

指令指针将大部分时间花在哪条指令上?我可以为他们想出很好的论据:

  • mov RAX, [RBX]可能需要 100 多个周期,因为它是缓存未命中。
  • mov RSI, [RBX + RCX]也需要 100 个周期,但可能与前一条指令并行执行。指令指针位于其中一个或另一个上意味着什么?
  • xor R8, R8可能会乱序执行并在内存加载完成之前完成,但指令指针可能会保留在这里,直到所有先前的指令也完成。
  • add RDX, RAX产生流水线停顿,因为它是RAX在慢速缓存未命中加载到它之后实际使用的值的指令。
  • add RDI, RSI也会停止,因为它取决于加载到RSI.
4

2 回答 2

11

CPU 保持一种虚构,即只有架构寄存器(RAX、RBX 等),并且有一个特定的指令指针 (IP)。程序员和编译器以这种虚构为目标。

然而,正如您所指出的,现代 CPU 不会串行或按顺序执行。直到程序员/用户请求 IP,就像量子物理学一样,IP 是一波正在执行的指令;所有这些都是为了让处理器可以尽可能快地运行程序。当您请求当前 IP(例如,通过调试器断点或分析器中断)时,处理器必须重新创建您期望的虚构,以便折叠此波形(所有“正在运行”指令),将寄存器值收集回架构名称,并构建用于执行调试器例程的上下文等。

在这种情况下,有一个 IP 指示处理器应恢复执行的指令。在乱序执行期间,该指令是尚未完成的最旧指令,即使在中断时处理器可能正在获取远远超过该点的指令。

例如,也许中断指示mov RSI, [RBX + RCX];为IP,但xor已经执行完毕;但是,当处理器在中断后恢复执行时,它将重新执行异或。

于 2013-06-09T14:11:42.337 回答
1

这是一个很好的问题,但在我所做的那种性能调整中,这并不重要。这并不重要,因为您正在寻找的是速度错误。这些是代码正在做的事情,需要花费时钟时间,并且可以做得更好或根本不做。示例:
- 花费 I/O 时间在 DLL 中查找实际上不需要查找的资源。
- 在内存分配例程中花费时间制作和释放可以简单地重复使用的对象。
- 重新计算可以记忆的函数中的东西。
......这只是我脑海中的一些

你最大的敌人是自我祝贺的倾向,说“我不会有意识地写任何错误。我为什么要写?” 当然,您知道这就是您测试软件的原因。但是对于速度错误也是如此,如果你不知道如何找到那些你认为没有的错误,这是一种说法“我的代码没有可能的加速,除非分析器可以告诉我如何刮胡子几个周期。”

在我半个世纪的经验中,没有任何代码在最初编写时不包含速度错误。更重要的是,还有一个巨大的乘数效应,你消除的每一个速度错误都会使其余的更明显。作为一个人为的例子,假设 bug A 占时钟时间的 90%,而 bug B 占 9%。如果你只修复 B,那很重要 - 代码快 11%。如果你只修复 A,那很好——它快 10 倍。但是,如果您同时解决这两个问题,那就太好了——速度提高了 100 倍。固定 A 使 B 变大。

所以你在性能调优中最需要的是找到速度错误,不要错过任何. 完成所有这些后,您就可以开始进行周期剃须了。

于 2019-06-02T13:01:49.150 回答