Pat*_*ick 19 performance visual-studio-2010 stack-frame
正如很久以前建议的那样,我总是在没有帧指针的情况下构建我的发布可执行文件(如果用/ Ox编译,这是默认的).
但是,现在我在http://research.microsoft.com/apps/pubs/default.aspx?id=81176这篇论文中读到,帧指针对性能没有太大影响.因此,完全优化它(使用/ Ox)或使用帧指针(使用/ Ox/Oy-)完全优化它并不会对性能产生重大影响.
微软似乎表示添加帧指针(/ Oy-)使调试变得更容易,但事实确实如此吗?
我做了一些实验并注意到:
关于帧指针的一般建议是什么?
使用Visual Studio 2010.
Joh*_*rak 27
简答:通过省略框架指针,
您需要使用堆栈指针来访问局部变量和参数.编译器并不介意,但如果你在编码中,这会让你的生活变得更加困难.如果你不使用宏,那就更难了.
每个函数调用可以节省四个字节(32位架构)的堆栈空间.除非你使用深度递归,否则这不是一场胜利.
您将内存写入保存到高速缓存的内存(堆栈),并且(理论上)在函数入口/出口处保存几个时钟周期,但您可以增加代码大小.除非你的功能经常做得很少(在这种情况下应该内联),这不应该是显而易见的.
你释放了一个通用寄存器.如果编译器可以利用寄存器,它将产生既小又可能更快的代码.但是,如果花费大部分CPU时间与主存储器(甚至是硬盘驱动器)进行通信,那么省略帧指针并不会使您远离它.
调试器将失去一种生成堆栈跟踪的简便方法.调试器可能仍然能够从不同的源(例如PDB文件)生成堆栈跟踪.
答案很长:
典型的功能进入和退出是:
PUSH SP ;push the frame pointer
MOV FP,SP ;store the stack pointer in the frame pointer
SUB SP,xx ;allocate space for local variables et al.
...
LEAVE ;restore the stack pointer and pop the old frame pointer
RET ;return from the function
Run Code Online (Sandbox Code Playgroud)
没有堆栈指针的入口和出口可能如下所示:
SUB SP,xx ;allocate space for local variables et al.
...
ADD SP,xx ;de-allocate space for local variables et al.
RET ;return from the function.
Run Code Online (Sandbox Code Playgroud)
您将保存两个指令,但您还复制了一个文字值,因此代码不会变得更短(完全相反),但您可能已经保存了几个时钟周期(如果它导致指令缓存中的缓存未命中,则不会) .但是你确实在堆栈上节省了一些空间.
你可以释放通用寄存器.这只有好处.
在regcall/fastcall中,这是一个额外的寄存器,您可以在其中存储函数的参数.因此,如果您的函数需要七个(在x86上;在大多数其他体系结构上更多)或更多参数(包括this),则第七个参数仍然适合寄存器.同样,更重要的是,也适用于局部变量.数组和大对象不适合寄存器(但指向它们的指针),但如果你的函数使用七个不同的局部变量(包括计算复杂表达式所需的临时变量),编译器很可能会生成更小的代码.较小的代码意味着较低的指令缓存占用空间,这意味着降低了错失率,因此甚至更少的内存访问(但英特尔凌动有一个32K指令缓存,这意味着您的代码可能适合任何方式).
x86架构具有[BX/BP/SI/DI]和[BX/BP + SI/DI]寻址模式.这使得BP寄存器对于缩放数组索引非常有用,特别是如果数组指针位于SI或DI寄存器中.两个偏移寄存器优于一个.
利用寄存器可以避免内存访问,但是如果一个变量值得存储在寄存器中,那么它很可能在L1缓存中一直存活(特别是因为它将在堆栈中).移动到缓存或从缓存移动仍然是成本,但由于现代CPU做了很多移动优化和并行化,因此L1访问可能与寄存器访问一样快.因此,不移动数据的速度效益仍然存在,但不是很大.我可以很容易地想象CPU完全避免数据缓存,至少就读取而言(并且写入缓存可以并行完成).
使用的寄存器是需要保留的寄存器.如果要在再次使用它之前将其推送到堆栈,则不值得在寄存器中存储太多.在通过调用者保持调用约定(例如上面的那个)中,这意味着寄存器作为持久存储在一个很多调用其他函数的函数中并不那么有用.
另请注意,x86具有用于浮点寄存器的单独寄存器空间,这意味着浮点数无法使用BP寄存器而无需额外的数据移动指令.只有整数和内存指针才能.
通过省略帧指针而丢失的是可调试性.这个答案显示了原因:
如果代码崩溃,所有调试器需要做的就是生成堆栈跟踪:
PUSH FP ; log the current frame pointer as well
$1: CALL log_FP ; log the frame pointer currently on stack
LEAVE ; pop the frame pointer to get the next one
CMP [FP+4],0
JNZ $1 ; until the stack cannot be popped (the return address is some specific value)
Run Code Online (Sandbox Code Playgroud)
如果代码在没有帧指针的情况下崩溃,则调试器可能无法生成堆栈跟踪,因为它可能不知道(即,它需要定位函数入口/出口点)需要从堆栈指针中减去多少.如果调试器不知道帧指针未被使用,它甚至可能自行崩溃.
| 归档时间: |
|
| 查看次数: |
4752 次 |
| 最近记录: |