编译器的“-O0”选项和“-Og”选项有什么区别?

9
当我想调试C或C ++程序时,我被教导使用-O0关闭优化,并使用-ggdb将符号插入可执行文件中,这些符号经过优化,适用于GNU gdb调试器,我使用它(或者您可以使用-glldb用于LLVM / clang的lldb调试器,或者只是-g用于一般调试符号,但是这不会像-ggdb那样好...)。但是,最近我偶然听到有人说要使用-Og(而不是-O0),这让我措手不及。不过,man gcc中确实有这个选项!:

-Og 优化调试体验。-Og启用不会干扰调试的优化。它应该是标准编辑-编译-调试周期的优化级别,提供合理的优化水平,同时保持快速编译和良好的调试体验。

那么,有什么区别吗?这里是来自man gcc-O0描述:

-O0 减少编译时间并使调试产生预期结果。这是默认设置。

man gcc 明确表示 -Og “应该是标准编辑-编译-调试循环的优化级别选择”,但是。

这听起来像是 -O0 确实是“没有优化”,而 -Og 是“开启了一些优化,但仅限于不干扰调试的优化。” 这是正确的吗?那么,我应该使用哪个,为什么?

相关:

  1. 相关但不是重复的问题!(仔细阅读,它根本不是重复的): -O0、-O1 和 -g 之间的区别是什么?
  2. 我在与 Bazel 一起使用的调试 --copt= 设置上的答案:gdb:当前上下文中没有符号“i”

6
手册中有更多细节:与-O0类似,-Og完全禁用了一些优化传递,以使控制它们的各个选项无效。否则,-Og启用除可能干扰调试之外的所有-O1优化标志。然后列出了禁用的确切标志。 - kaylum
啊,这很有用!这比man gcc里的信息更多! - Gabriel Staples
如果你仔细阅读了问题中的摘录,我认为答案已经非常明确了。-Og 优化了调试体验,而 -O0 则最小化了编译速度。如果你想享受调试过程,就要使用 -Og;如果你想快速找出是否编译成功,就要使用 -O0 - Antti Haapala -- Слава Україні
@AnttiHaapala,没错,但是从其他开发者那里得到确认是很好的,特别是当我对新信息感到如此惊讶时(例如:-Og居然存在)。 - Gabriel Staples
2个回答

12

快速摘要

不要使用-Og-Og会破坏调试。

使用-ggdb -O0(如果使用gdb调试器,则首选),或者使用-g3 -O0

使用-g -O0也可以,但是-g单独默认为调试级别2(-g2),这意味着与-g3相比,-g缺少“额外的信息,例如程序中存在的所有宏定义。”(参见man gcc并搜索-glevel)。

详细信息

@kaylum在我的问题下提供了一些很好的见解!而我最关心的关键部分是:

[-Og]比- O0更适合生成可调试的代码,因为某些收集调试信息的编译器传递在-O0时被禁用。

https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Options

所以,从现在开始,我除了使用-ggdb之外,还要使用-Og(而不是-O0)。

2020年8月13日更新:

算了吧!没关系。我还是坚持使用-O0

使用-Og会导致代码中到处都是<optimized out>无法获取非左值变量"var"的地址。错误!我无法打印我的变量或检查它们的内存!例如:

(gdb) print &angle
Can't take address of "angle" which isn't an lvalue.
(gdb) print angle_fixed_p
$6 = <optimized out>

使用-O0,然而,一切都正常运作!
(gdb) print angle
$7 = -1.34869879e+20
(gdb) print &angle
$8 = (float *) 0x7ffffffefbbc
(gdb) x angle
0x8000000000000000:     Cannot access memory at address 0x8000000000000000
(gdb) x &angle
0x7ffffffefbbc: 0xe0e9f642

所以,回到使用-O0而不是-Og

相关:

  1. [他们也推荐-O0,我同意] gdb中的<value optimized out>是什么意思?

5
我建议在调试时使用-O0选项。使用-Og时,断点往往会触发错误的行。 - Piotr Siupa
2
是的,-Og 代码生成更像 -O1,而不是你在 -O0 中真正严格的调试支持。(为什么clang使用-O0(对于这个简单的浮点求和)会产生低效的汇编代码?)。-Og 允许编译器优化本地临时变量。您可以通过 gcc -Og -S -fverbose-asm 看到其中一些内容,它仍然为其发明的临时变量发明名称,而不是在寄存器上操作时使用原始的 C 变量名称。尽管在简单情况下,似乎 -Og 更像 C 源代码而不是 -O1:https://godbolt.org/z/zxcqb66jx - Peter Cordes

1
由于编辑的原因,上面Gabriel's excellent answer中关于添加-ggdb的说明并不十分清晰。 我发现如果GDB是您首选的调试器,那么除了-O0之外,始终使用-ggdb33很重要,比ggdb更好)肯定是有优势的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接