我刚刚问了一个与编译器如何优化某些C++ 代码有关的问题,并且我正在寻找有关如何验证编译器是否执行某些优化的任何问题。我试图查看使用 g++ ( g++ -c -g -O2 -Wa,-ahl=file.s file.c
) 生成的程序集列表,以可能了解幕后发生的事情,但输出对我来说太神秘了。人们使用什么技术来解决这个问题,关于如何解释优化代码的汇编列表或讨论这个问题的特定于 GCC 工具链的文章是否有很好的参考?
8 回答
GCC 的优化以一种名为GIMPLE的格式传递代码的中间表示。
使用-fdump-*
选项系列,您可以要求 GCC 输出树的中间状态。
例如,将此喂给gcc -c -fdump-tree-all -O3
unsigned fib(unsigned n) {
if (n < 2) return n;
return fib(n - 2) + fib(n - 1);
}
并观察它逐渐从简单的指数算法转变为复杂的多项式算法。(真的!)
一个有用的技术是在良好的采样分析器下运行代码,例如 Linux 下的 Zoom 或 Mac OS X 下的 Instruments(使用 Time Profiler 工具)。这些分析器不仅可以显示代码中的热点,还可以将源代码映射到反汇编对象代码。突出显示源代码行显示映射到源代码行的(不一定是连续的)代码行(反之亦然)。在线操作码参考和优化技巧是一个不错的奖励。
不是 gcc,但在 Visual Studio 中进行调试时,您可以选择散布程序集和源代码,这可以很好地了解为什么语句生成了什么。但有时它并不完全正确对齐。
gcc 工具链的输出和objdump -dS
粒度不同。 这篇关于让 gcc 输出源和程序集的文章与您使用的选项相同。
添加-L
选项(例如,gcc -L -ahl
)可能会提供更易于理解的列表。
等效的 MSVC 选项是/FAcs
(它稍微好一点,因为它散布在源代码、机器语言和二进制文件中,并包含一些有用的注释)。
我大约三分之一的工作包括做你正在做的事情:处理 C 代码,然后查看汇编输出以确保它得到了正确优化(这比在所有地方都编写内联汇编更可取)。
游戏开发博客和文章可能是该主题的一个很好的资源,因为游戏实际上是在恒定内存中的实时应用程序——我对此有一些注释,Mike Acton和其他人也有。我通常喜欢在浏览列表时将英特尔的指令集参考保持在一个窗口中。
最有帮助的事情是首先对汇编编程有一个很好的基本理解——不是因为你想编写汇编代码,而是因为这样做会使阅读反汇编变得更加容易。不过,我很难找到一本好的现代教科书。
为了输出应用的优化,您可以使用:
-fopt 信息优化
查看那些尚未应用的
-fopt-info-missed
注意输出被发送到标准错误流,所以要看到它,你实际上必须重定向它:(提示 2>&1)
这是一个很好的例子:
g++ -O3 -std=c++11 -march=native -mtune=native
-fopt-info-optimized h2d.cpp -o h2d 2>&1
h2d.cpp:225:3: note: loop vectorized
h2d.cpp:213:3: note: loop vectorized
h2d.cpp:198:3: note: loop vectorized
h2d.cpp:186:3: note: loop vectorized
-g
应用后,您可以检查交错输出,objdump -dS|c++filt
但这不会让您走得那么远。享受吧!
另一个答案中提到了从 RotateRight ( http://rotateright.com ) 缩放,但要对此进行扩展:它在他们所谓的“代码浏览器”中向您显示了源到程序集的映射。即使您不是 asm 专家,它也非常方便,因为他们还将汇编文档集成到应用程序中。并且汇编列表带有注释和几种 CPU 类型的时序。
您可以使用 Zoom 打开您的对象或可执行文件,然后查看编译器对您的代码所做的处理。
与其尝试通读汇编程序转储,不如在调试器中运行您的程序。您可以暂停执行、单步执行指令、在要检查的代码上设置断点等。许多调试器可以在生成的程序集旁边显示您的原始 C 代码,这样您就可以更轻松地查看编译器为优化您的代码所做的工作。
此外,如果您正在尝试测试特定的编译器优化,您可以创建一个简短的虚拟函数,其中包含适合您感兴趣的优化的代码类型(除此之外,它越简单,程序集就越容易阅读)。编译程序一次,优化开启,关闭一次;在构建之间比较为虚拟函数生成的汇编代码应该向您展示编译器的优化器做了什么。
维克多,在您的情况下,您正在寻找的优化只是堆栈上较小的本地内存分配。如果空类使用的空间被优化掉,您应该在函数入口处看到较小的分配,在函数退出处看到较小的释放。
至于一般问题,我已经阅读(和编写)汇编语言超过(gulp!)30 年了,我只能说这需要练习,尤其是阅读编译器的输出。