gpt4 book ai didi

performance - 省略帧指针真的对性能有积极影响,对调试能力有消极影响吗?

转载 作者:行者123 更新时间:2023-12-03 12:41:33 27 4
gpt4 key购买 nike

正如很久以前所建议的那样,我总是在没有框架指针的情况下构建我的发布可执行文件(如果使用/Ox 编译,这是默认设置)。
然而,现在我读到了论文http://research.microsoft.com/apps/pubs/default.aspx?id=81176 ,帧指针对性能没有太大影响。因此,完全优化(使用/Ox)或使用帧指针完全优化(使用/Ox/Oy-)并没有真正对性能产生影响。
Microsoft 似乎表示添加帧指针 (/Oy-) 可以使调试更容易,但情况真的如此吗?
我做了一些实验并注意到:

  • 在一个简单的 32 位测试可执行文件(使用/Ox/Ob0 编译)中,省略帧指针确实提高了性能(大约 10%)。但是这个测试可执行文件只执行一些函数调用,没有别的。
  • 在我自己的应用程序中,添加/删除帧指针似乎没有太大影响。添加帧指针似乎使应用程序快 5%,但这可能在误差范围内。

  • 关于帧指针的一般建议是什么?
  • 是否应该在发布可执行文件中省略 (/Ox),因为它们确实对性能有积极影响?
  • 是否应该在发布可执行文件中添加 (/Ox/Oy-),因为它们提高了调试能力(使用故障转储文件进行调试时)?

  • 使用 Visual Studio 2010。

    最佳答案

    简答:通过省略帧指针,

    您需要使用堆栈指针来访问局部变量和参数。编译器不介意,但如果你在汇编中编码,这会让你的生活变得更艰难。如果你不使用宏,那就更难了。

    您可以为每个函数调用节省四个字节(32 位架构)的堆栈空间。除非您使用深度递归,否则这不是胜利。

    您将内存写入保存到缓存内存(堆栈),并且(理论上)在函数进入/退出时节省了一些时钟滴答,但您可以增加代码大小。除非您的函数很少经常执行(在这种情况下它应该被内联),否则这不应该引起注意。

    您释放了一个通用寄存器。如果编译器可以使用该寄存器,它将生成更小且可能更快的代码。但是,如果大部分 CPU 时间都花在与主内存(甚至是硬盘驱动器)的对话上,那么省略帧指针并不能避免您这样做。

    调试器将失去生成堆栈跟踪的简单方法。调试器可能仍然能够从不同的来源(例如 PDB file )生成堆栈跟踪。

    长答案:

    典型的函数入口和导出是:

    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

    没有堆栈指针的入口和导出可能如下所示:
    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.

    您将保存两条指令,但您还复制了一个文字值,因此代码不会变短(恰恰相反),但您可能已经保存了几个时钟周期(或者不会,如果它导致指令缓存中的缓存未命中) .不过,您确实在堆栈上节省了一些空间。

    你确实释放了一个通用寄存器。这只有好处。

    在 regcall/fastcall 中,这是一个额外的寄存器,您可以在其中存储函数的参数。因此,如果您的函数采用七个(在 x86 上;在大多数其他架构上更多)或更多参数(包括 this),则第七个参数仍然适合寄存器。更重要的是,这同样适用于局部变量。数组和大对象不适合寄存器(但指向它们的指针可以),但如果您的函数使用七个不同的局部变量(包括计算复杂表达式所需的临时变量),编译器可能会生成更小的代码.较小的代码意味着较低的指令缓存占用空间,这意味着降低未命中率,从而减少内存访问(但 Intel Atom has a 32K instruction cache ,这意味着您的代码可能无论如何都适合)。

    x86 架构具有 [BX/BP/SI/DI][BX/BP + SI/DI]寻址模式。这使得 BP 寄存器成为缩放数组索引非常有用的地方,尤其是当数组指针驻留在 SI 或 DI 寄存器中时。两个偏移寄存器比一个好。

    使用寄存器可以避免内存访问,但是如果变量值得存储在寄存器中,那么它很可能会在 L1 缓存中保持良好状态(尤其是因为它将在堆栈中)。移入/移出缓存仍然存在成本,但由于现代 CPU 进行了大量移动优化和并行化,因此 L1 访问可能与寄存器访问一样快。因此,不移动数据带来的速度优势仍然存在,但没有那么大。我可以很容易地想象 CPU 完全避免数据缓存,至少就读取而言(并且写入缓存可以并行完成)。

    使用的寄存器是需要保存的寄存器。如果您打算在再次使用之前将其压入堆栈,则在寄存器中存储太多内容是不值得的。在调用者保留调用约定(例如上面的约定)中,这意味着寄存器作为持久存储在调用其他函数很多的函数中没有那么有用。

    还要注意,x86 有一个单独的寄存器空间用于浮点寄存器,这意味着浮点数不能在没有额外数据移动指令的情况下使用 BP 寄存器。只有整数和内存指针可以。

    省略帧指针会丢失可调试性。 This answer说明原因:

    如果代码崩溃,调试器需要做的就是生成堆栈跟踪:
        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)

    如果代码在没有帧指针的情况下崩溃,调试器可能无法生成堆栈跟踪,因为它可能不知道(即,它需要定位函数入口/导出点)需要从堆栈指针中减去多少。如果调试器不知道没有使用帧指针,它甚至可能会崩溃。

    关于performance - 省略帧指针真的对性能有积极影响,对调试能力有消极影响吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13006371/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com