30

问题:

访问堆栈的速度与访问内存的速度相同吗?

例如,我可以选择在堆栈中做一些工作,或者我可以直接使用内存中的标记位置进行工作。

所以,具体来说:push ax速度是否与mov [bx], ax? 同样pop ax速度与mov ax, [bx]? (假设 bx 在near内存中保存一个位置。)

提问的动机:

在 C 中,不鼓励使用参数的琐碎函数是很常见的。

我一直认为这是因为不仅参数必须被压入堆栈然后在函数返回后从堆栈中弹出,而且因为函数调用本身必须保留 CPU 的上下文,这意味着更多的堆栈使用。

但是假设人们知道标题问题的答案,应该可以根据直接内存访问的等效数量来量化函数用于设置自身(推送/弹出/保留上下文等)的开销。因此,标题问题。


编辑:澄清:near上面使用的与 16 位 x86 架构far分段内存模型相反。)

4

2 回答 2

20

如今,您的 C 编译器可以胜过您。它可以内联简单的函数,如果这样做,将不会有函数调用或返回,并且可能不会有与传递和访问正式函数参数相关的额外堆栈操作(或当函数被内联但可用寄存器已用尽)如果一切都可以在寄存器中完成,或者更好的是,如果结果是一个常量值并且编译器可以看到并利用它。

函数调用本身在现代 CPU 上可能相对便宜(但不一定是零成本),如果它们是重复的,并且如果有单独的指令缓存和各种预测机制,有助于高效的代码执行。

除此之外,我希望选择“local var vs global var”对性能的影响取决于内存使用模式。如果 CPU 中有内存缓存,则堆栈很可能在该缓存中,除非您在其上分配和解除分配大型数组或结构或进行深度函数调用或深度递归,从而导致缓存未命中。如果经常访问感兴趣的全局变量或者经常访问它的邻居,我希望该变量大部分时间也在缓存中。同样,如果您正在访问无法放入缓存的大范围内存,您将遇到缓存未命中并可能降低性能(可能是因为可能有也可能没有更好的缓存友好的方式来执行您的操作想要做)。

如果硬件相当笨拙(没有或很小的缓存、没有预测、没有指令重新排序、没有推测执行,什么都没有),显然你想减少内存压力和函数调用的数量,因为每个人都会算数。

还有一个因素是指令长度和解码。访问堆栈位置(相对于堆栈指针)的指令可以比访问给定地址的任意内存位置的指令短。较短的指令可以更快地解码和执行。

我想说对于所有情况都没有明确的答案,因为性能取决于:

  • 你的硬件
  • 你的编译器
  • 您的程序及其内存访问模式
于 2012-10-07T06:49:13.963 回答
14

对于时钟周期好奇...

对于那些希望查看特定时钟周期的人,可在此处获得各种现代 x86 和 x86-64 CPU 的指令/延迟表(感谢 hirschhornsalz 指出这些)。

然后,您会在 Pentium 4 芯片上获得:

  • push axmov [bx], ax(红框)的效率几乎相同,延迟和吞吐量相同。
  • pop axmov ax, [bx](蓝框)同样有效,尽管mov ax, [bx]延迟是pop ax

Pentium 4 指令时序表

至于评论中的后续问题(第三条评论):

  • 间接寻址(ie mov [bx], ax)与直接寻址(ie )没有本质区别mov [loc], ax,其中 loc 是一个保存立即值的变量,例如loc equ 0xfffd

结论:将此与Alexey 的彻底回答结合起来,使用堆栈的效率并让编译器决定何时内联函数是一个非常可靠的案例。

(旁注:事实上,即使早在 1978 年的 8086,使用堆栈的效率仍然不低于相应的 mov 到内存,从这些旧的 8086 指令时序表中可以看出。)


了解延迟和吞吐量

要了解现代 CPU 的时序表,可能还需要更多知识。这些应该有助于:

于 2012-10-10T12:11:38.860 回答