assembly “输入”vs“按下ebp;mov ebp,尤指;“特别用途”和“离开”与“特别用途移动”和“EBP”;弹出ebp”

yhuiod9q  于 2023-03-02  发布在  其他
关注(0)|答案(4)|浏览(165)

enter和的区别是什么

push ebp
mov  ebp, esp
sub  esp, imm

指令?是否有性能差异?如果有,哪个更快?为什么编译器总是使用后者?
类似于leave

mov  esp, ebp
pop  ebp

指示。

uz75evzq

uz75evzq1#

这两者在性能上存在差异,尤其是enter。在现代处理器上,这解码为大约10到20 µ op,而三指令序列大约为4到6,具体取决于架构。有关详细信息,请参阅Agner Fog's指令表。
此外,与三指令序列的3时钟相关性链相比,enter指令通常具有相当高的等待时间,例如在核心2上为8时钟。
此外,为了调度的目的,三个指令序列可以由编译器展开,当然这取决于周围的代码,以允许指令的更多并行执行。

ubof19bj

ubof19bj2#

在设计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的替代方法是:
;输入n,0 ;486上的14个周期

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

;输入n,1 ;486上的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

;输入n,3 ;486上的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

长的路可能会增加你的文件大小,但更快。
最后一点注意,程序员不再使用显示器了,因为这是一个非常缓慢的工作,使回车现在相当无用。
来源:https://courses.engr.illinois.edu/ece390/books/artofasm/CH12/CH12-3.html

ulydmbyx

ulydmbyx3#

使用这两种方法都没有真实的的速度优势,尽管长方法可能会运行得更好,因为现在的CPU更“优化”到更短更简单的指令,这些指令在使用中更通用(加上如果你幸运的话,它允许执行端口饱和)。
LEAVE(现在仍在使用,请参阅windows dll)的优点是它比手动拆除堆栈框架要小,当空间有限时,这会有很大帮助。
英特尔的操作手册(准确地说是2A卷)将有更多关于指令的细节,Dr Agner Fogs Optimization manuals也应该如此

piwo6bdm

piwo6bdm4#

**enter在所有CPU上都非常慢,**除了以牺牲速度为代价优化代码大小之外,没有人使用它。(如果需要帧指针,或者希望允许更紧凑的寻址模式来寻址堆栈空间。)
**leave足够快,值得使用,并且GCC * 确实 * 使用它(如果ESP / RSP还没有指向保存的EBP/RBP;否则它只使用pop ebp)。

leave在现代Intel CPU上仅为3个uop(在某些AMD上为2个)。
mov / pop总共只有2个微指令(在现代的x86上,一个“堆栈引擎”跟踪ESP/RSP的更新)。所以leave只是比单独做事情多一个uop。我已经在Skylake上测试过了,将循环中的call/ret与使用mov/popleave设置传统帧指针并拆除其堆栈帧的函数进行比较。uops_issued.anyperf计数器在使用leave时比mov/pop多显示一个前端uop。(我运行了自己的测试,以防其他测量方法在leave测量中计数堆栈同步uop,但在真实的函数中使用它会对此进行控制。)
旧CPU保持mov / pop分离可能会受益更多的可能原因:

  • 在大多数没有uop缓存的CPU中(比如英特尔在Sandybridge之前,AMD在Zen之前),多uop指令可能是解码瓶颈。它们只能在第一个(“复杂”)解码器中解码,所以可能意味着在此之前的解码周期产生的uop比正常情况下要少。
  • 一些Windows调用约定是使用ret n的被调用方弹出堆栈参数(例如,ret 8在弹出返回地址后执行ESP/RSP += 8)。这是一条多微指令,不像现代x86上ret附近的普通指令。因此,上述原因有两个:leave和ret 12无法在同一周期解码
  • 这些原因也适用于构建微指令缓存条目的传统解码。
  • P5 Pentium也偏爱x86的一个类似RISC的子集,甚至无法将复杂的指令分解成单独的uop *。
    对于现代CPUleave占用uop缓存中的1个额外uop。并且所有3个都必须在uop缓存的同一行中,这可能导致仅部分填充前一行。因此,更大的x86代码大小 * 可以 * 实际上改善装入uop缓存的情况。或者不改善,取决于如何排列。

节省2个字节(或在64位模式下节省3个字节)可能值得也可能不值得每个函数多执行1个uop。
GCC支持leave,clang和MSVC支持mov/pop(即使是以牺牲速度为代价的clang -Oz代码大小优化,例如做push 1 / pop rax(3字节)而不是5字节的mov eax,1)。
ICC支持移动/弹出,但对于-Os,将使用leavehttps://godbolt.org/z/95EnP3G1f

相关问题