我只是试图在x86汇编中进行非常快速的基于计算的编程
但我需要在调用程序之前先推累加器,计数器和数据寄存器。手动推送它们更快:

push eax
push ecx
push edx

或只是使用,
pushad

和弹出相同。谢谢

最佳答案

如果您关心性能,pusha / popa几乎从不有用。它们仅在以速度为代价优化代码大小时才有用,例如保存/恢复功能周围的寄存器。但是对于非void函数而言,这非常不方便,因为它们会重新加载所有寄存器,因此您必须将返回值存储在内存中(例如在将要加载到eax的堆栈插槽中,或者在popad之后要重新加载的其他位置)。

仅推送需要保存或要作为函数args传递的寄存器。或者,在inline-assembly中,只需声明任何临时注册表的"=r"(dummy1)伪输出操作数,或在特定寄存器上使用Clobber,让编译器为您管理寄存器。正常情况下,编译器可以选择一些寄存器,它可以让您不费吹灰之力。 (或者在笨拙的MSVC样式的内联汇编中,编译器无法为您分配寄存器,因此您必须手动进行选择。编译器会分析您的汇编以查找Clobbers。)

您通常不需要保存/恢复eax;为了提高性能,如果您不能首先计算mov esi, eax中的值,则可能应该esi / call /使用esi中的值。 ,即对需要保留call 的值使用调用保留的寄存器,因此重要值的存储/重装不在关键路径上。取而代之的是,存储/重新加载位于调用者的调用保留寄存器之一的关键路径上,您(或编译器)在整个函数中,在任何循环之外,push / pop

即使您确实想推送所有8个整数寄存器(包括esp!),在现代CPU上使用8个单独的push指令实际上也更快。 pusha / popa是微码的which can be a problem for the front-end。 (尽管对于uop缓存来说8个单字节指令也可能是一个问题。但是在实际代码中,通常只需要压入几个寄存器,而不是全部压入)。

如果您要针对过时的CPU(例如原始的有序Pentium和Pentium II / III)进行优化,则pusha / popa的速度将达到8 push r或8 pop r,实际上uops更少,因为它们没有堆栈引擎消除ESP更新指令。

Agner Fog's instruction tables来看:现代CPU具有单字节push regpop reg,因为这些指令一直被编译器使用,因此对于性能至关重要。推/弹出吞吐量通常与存储/加载吞吐量匹配(通常每个时钟1个存储或每个时钟2个加载)。但是编译器不使用pusha / popa,因此CPU设计人员没有特殊的支持来使其快速运行。如果仅运行popapopa吞吐量将限制为每个时钟仅1个负载。 (我认为在Intel CPU上,最可能的性能衡量指标是popa不使用堆栈引擎,因此它成为依赖esp的瓶颈。)

英特尔:

  • Skylake:pusha:11 uops,吞吐量为8c。 popa:18微秒/ 8c吞吐率。
  • Sandybridge:pusha:16 uops / 8c吞吐量。 popa:18 uops / 9c吞吐量。
  • Nehalem:pusha:18 uops / 8c吞吐量。 popa:10微秒/ 8c吞吐率。
  • Silvermont / KNL:pusha:10 uops / 10c吞吐量。 popa:17 oups / 14c吞吐量。
  • Pentium4:pusha:4/10 uops / 19c吞吐量。 popa:4/16 uops / 14c吞吐量。
  • P5 Pentium 1 / MMX:5-9个周期,不可配对。 “如果SP被4整除(不完美配对),则为9。”

  • AMD:pusha / popa在某些AMD CPU(尤其是K8)上出奇地好。
  • Ryzen:pusha:9 uops,吞吐量为8c。 popa:9微码, 4c吞吐量。 (与英特尔不同,AMD的新设计的popa不比8x pop差。)
  • Jaguar:pusha:9微秒/ 8c吞吐量。 popa:9 oups / 8c吞吐量。 (Jaguar通常每个时钟只能加载一个负载。)
  • 打桩机:pusha:9微秒/ 9c吞吐量。 popa:14微秒/ 8c吞吐率。 (Agner列出了Bulldozer系列的常规pop reg吞吐量为每个时钟1个时钟,尽管我认为他们确实有一个堆栈引擎并且每个时钟可以进行2次加载。也许堆栈引擎一次只能处理一个堆栈指令?)
  • K8:pusha:9 oups / 4c吞吐量!! (IDK怎么可能,这是表中的错误或错字,还是K8合并32位寄存器并进行四个64位存储)。 popa:9 oups / 4c吞吐量。这些数字似乎是真实的:InstLatx86 measurements与Clawhammer(第一代K8微体系结构)上pushad / popad的4c吞吐量一致。显然,AMD付出了一些努力来优化pushad


  • 您标记了此inline-assembly。通常,您应该避免在inline-asm中使用call,因此C编译器知道该调用。

    关于performance - x86 Assembly pushad/popad,它有多快?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48449166/

    10-11 18:48