53

enter和有什么区别

push ebp
mov  ebp, esp
sub  esp, imm

指示?有性能差异吗?如果是这样,哪个更快,为什么编译器总是使用后者?

leave_

mov  esp, ebp
pop  ebp

指示。

4

4 回答 4

51

存在性能差异,尤其是对于enter. 在现代处理器上,这解码到大约 10 到 20 µops,而三个指令序列大约是 4 到 6,具体取决于架构。有关详细信息,请参阅Agner Fog 的说明表。

此外enter,与三个指令序列的 3 个时钟依赖链相比,该指令通常具有相当高的延迟,例如 core2 上有 8 个时钟。

此外,出于调度目的,编译器可以扩展三个指令序列,当然这取决于周围的代码,以允许指令的更多并行执行。

于 2011-05-11T12:49:30.447 回答
6

使用它们中的任何一个都没有真正的速度优势,尽管长方法可能会运行得更好,因为这些天 CPU 对使用更通用的更短更简单的指令更加“优化”(加上它允许执行饱和端口,如果你幸运的话)。

(仍在使用,只需查看 windows dll)的优点LEAVE是它比手动拆除堆栈框架更小,这在您的空间有限时有很大帮助。

英特尔说明手册(准确地说是第 2A 卷)将在说明中提供更多细节,Agner Fogs 博士优化手册也应如此

于 2011-05-11T08:31:17.790 回答
6

在设计 80286 时,英特尔的 CPU 设计人员决定添加两条指令来帮助维护显示器。

这里是 CPU 内部的微代码:

; ENTER Locals, LexLevel

push    bp              ;Save dynamic link.
mov     tempreg, sp     ;Save for later.
cmp     LexLevel, 0     ;Done if this is lex level zero.
je      Lex0

lp:
dec     LexLevel
jz      Done            ;Quit if at last lex level.
sub     bp, 2           ;Index into display in prev act rec
push    [bp]            ; and push each element there.
jmp     lp              ;Repeat for each entry.

Done:
push    tempreg         ;Add entry for current lex level.

Lex0:
mov     bp, tempreg     ;Ptr to current act rec.
sub     sp, Locals      ;Allocate local storage

ENTER 的替代方法是:

; 在 486 上输入 n, 0 ;14 个周期

push    bp              ;1 cycle on the 486
sub     sp, n           ;1 cycle on the 486

; 在 486 上输入 n, 1 ;17 个周期

push    bp              ;1 cycle on the 486
push    [bp-2]          ;4 cycles on the 486
mov     bp, sp          ;1 cycle on the 486
add     bp, 2           ;1 cycle on the 486
sub     sp, n           ;1 cycle on the 486

; 在 486 上输入 n, 3 ;23 个周期

push    bp              ;1 cycle on the 486
push    [bp-2]          ;4 cycles on the 486
push    [bp-4]          ;4 cycles on the 486
push    [bp-6]          ;4 cycles on the 486
mov     bp, sp          ;1 cycle on the 486
add     bp, 6           ;1 cycle on the 486
sub     sp, n           ;1 cycle on the 486

等等。很长的路可能会增加您的文件大小,但速度更快。

最后一点,程序员不再真正使用显示,因为这是一个非常缓慢的工作,使得 ENTER 现在变得非常无用。

资料来源:https ://courses.engr.illinois.edu/ece390/books/artofasm/CH12/CH12-3.html

于 2014-05-02T12:38:48.410 回答
2

enter在所有 CPU 上都非常慢,没有人使用它,除了可能以牺牲速度为代价进行代码大小优化。(如果根本需要帧指针,或者希望允许更紧凑的寻址模式来寻址堆栈空间。)

leave 足够快,值得使用,并且 GCC确实使用它(如果 ESP / RSP 尚未指向保存的 EBP/RBP;否则它只是使用pop ebp)。

leave在现代英特尔 CPU 上只有 3 个微指令(在某些 AMD 上是 2 个)。(https://agner.org/optimize/https://uops.info/)。

mov / pop 总共只有 2 uops(在现代 x86 上,“堆栈引擎”跟踪对 ESP/RSP 的更新)。leave与单独做事相比,这只是多一个 uop 。mov我已经在 Skylake 上对此进行了测试,将循环中的 call/ret 与设置传统帧指针并使用/pop或拆除其堆栈帧的函数进行了比较leaveperf当您使用 leave 时,计数器uops_issued.any显示的前端 uop 比 mov/pop 多。(我进行了自己的测试,以防其他测量方法在他们的休假测量中计算堆栈同步 uop,但在实际功能控件中使用它。)

较旧的 CPU 在保持 mov / pop 分离时可能受益更多的可能原因:

  • 在大多数没有 uop 缓存的 CPU 中(即在 Sandybridge 之前的 Intel,在 Zen 之前的 AMD)中,多 uop 指令可能成为解码瓶颈。它们只能在第一个(“复杂”)解码器中解码,因此可能意味着之前的解码周期产生的微指令比正常情况少。

  • 一些 Windows 调用约定是 callee-pops 堆栈参数,使用ret n. (例如ret 8,在弹出返回地址后执行 ESP/RSP += 8)。这是一条多指令指令,不像ret现代 x86 上的普通指令。所以上面的原因是双重的:离开并且ret 12不能在同一个循环中解码

  • 这些原因也适用于传统解码以构建 uop-cache 条目。

  • P5 Pentium 还更喜欢 x86 的类似 RISC 的子集,甚至根本无法将复杂的指令分解为单独的微指令

对于现代 CPUleave在 uop 缓存中占用 1 个额外的 uop。并且所有 3 个都必须在 uop 缓存的同一行中,这可能导致仅部分填充前一行。所以更大的 x86 代码大小实际上可以改进打包到 uop 缓存中。与否,取决于事情的排列方式。

每个函数节省 2 个字节(或 3 个在 64 位模式下)可能值得也可能不值得 1 个额外的 uop。

GCC 支持leave、clang 和 MSVC 支持mov/ pop(即使以clang -Oz牺牲速度为代价进行代码大小优化,例如,使用push 1 / pop rax(3 bytes) 而不是 5-byte mov eax,1)。

ICC 支持 mov/pop,但-Os会使用leave. https://godbolt.org/z/95EnP3G1f

于 2021-05-06T19:41:36.050 回答