GCC 优化级别有多少?

Posted

技术标签:

【中文标题】GCC 优化级别有多少?【英文标题】:How many GCC optimization levels are there? 【发布时间】:2010-12-19 05:02:32 【问题描述】:

GCC 有多少个优化级别?

我试过 gcc -O1、gcc -O2、gcc -O3 和 gcc -O4

如果我使用一个非常大的数字,它将不起作用。

不过,我试过了

gcc -O100

它编译了。

有多少优化级别?

【问题讨论】:

@minitech 你在看哪个调频?即使在 Cygwin 上使用 man gcc(12000 行奇数行),您也可以搜索 -O 并找到以下状态的所有答案,然后是一些。 @minmaxavg 阅读源代码后,我不同意你的观点:任何大于3 的东西都与3 相同(只要它没有int 溢出)。见my answer。 实际上,GCC 有许多其他标志来微调优化。 -fomit-stack-pointer 将更改生成的代码。 【参考方案1】:

我们来解读一下GCC 5.1的源码

我们将尝试了解 -O100 上发生的情况,因为手册页上并不清楚。

我们将得出结论:

-O3INT_MAX 上的任何内容都与 -O3 相同,但将来很容易改变,所以不要依赖它。 如果您输入大于INT_MAX 的整数,GCC 5.1 会运行未定义的行为。 参数只能有数字,否则会正常失败。特别是,这不包括像 -O-1 这样的负整数

关注子程序

首先要记住,GCC 只是 cppascc1collect2 的前端。一个快速的./XXX --help 说只有collect2cc1 接受-O,所以让我们关注它们。

还有:

gcc -v -O100 main.c |& grep 100

给予:

COLLECT_GCC_OPTIONS='-O100' '-v' '-mtune=generic' '-march=x86-64'
/usr/local/libexec/gcc/x86_64-unknown-linux-gnu/5.1.0/cc1 [[noise]] hello_world.c -O100 -o /tmp/ccetECB5.

所以-O 被转发到cc1collect2

O in common.opt

common.opt 是internals documentation 中描述的GCC 特定CLI 选项描述格式,并由opth-gen.awk 和optc-gen.awk 转换为C。

它包含以下有趣的行:

O
Common JoinedOrMissing Optimization
-O<number>  Set optimization level to <number>

Os
Common Optimization
Optimize for space rather than speed

Ofast
Common Optimization
Optimize for speed disregarding exact standards compliance

Og
Common Optimization
Optimize for debugging experience rather than speed or size

指定所有O 选项。请注意-O&lt;n&gt; 与其他OsOfastOg 是如何在不同的家庭中的。

当我们构建时,这会生成一个 options.h 文件,其中包含:

OPT_O = 139,                               /* -O */
OPT_Ofast = 140,                           /* -Ofast */
OPT_Og = 141,                              /* -Og */
OPT_Os = 142,                              /* -Os */

作为奖励,当我们在 common.opt 中寻找 \bO\n 时,我们注意到以下行:

-optimize
Common Alias(O)

这告诉我们--optimize(双破折号,因为它在.opt 文件上以破折号-optimize 开头)是-O 的未记录别名,可用作--optimize=3

使用 OPT_O 的地方

现在我们 grep:

git grep -E '\bOPT_O\b'

将我们指向两个文件:

opts.c lto-wrapper.c

我们先来追踪opts.c

opts.c:default_options_optimization

所有opts.c 的使用都发生在内部:default_options_optimization

我们grep backtrack看谁调用了这个函数,我们看到唯一的代码路径是:

main.c:main toplev.c:toplev::main opts-global.c:decode_opts opts.c:default_options_optimization

main.ccc1 的入口点。好!

这个函数的第一部分:

OPT_O对应的字符串上调用atoiintegral_argument解析输入参数 将值存储在opts-&gt;x_optimize 中,其中optsstruct gcc_opts

结构 gcc_opts

grep 无效后,我们注意到struct 也是在options.h 处生成的:

struct gcc_options 
    int x_optimize;
    [...]

x_optimize 的来源:

Variable
int optimize

出现在common.opt,以及options.c

struct gcc_options global_options;

所以我们猜测这是包含整个配置全局状态的内容,int x_optimize 是优化值。

255 是内部最大值

opts.c:integral_argument 中,atoi 应用于输入参数,因此INT_MAX 是一个上限。如果你放任何更大的东西,GCC 似乎会运行 C 未定义的行为。哎哟?

integral_argument 还对atoi 进行了薄包装,如果任何字符不是数字,则拒绝该参数。所以负值会优雅地失败。

回到opts.c:default_options_optimization,我们看到一行:

if ((unsigned int) opts->x_optimize > 255)
  opts->x_optimize = 255;

因此优化级别被截断为255。在阅读opth-gen.awk 时,我遇到了:

# All of the optimization switches gathered together so they can be saved and restored.
# This will allow attribute((cold)) to turn on space optimization.

并在生成的options.h:

struct GTY(()) cl_optimization

  unsigned char x_optimize;

这解释了为什么截断:选项也必须转发到cl_optimization,它使用char 来节省空间。所以 255 实际上是一个内部最大值。

opts.c:maybe_default_options

回到opts.c:default_options_optimization,我们遇到了maybe_default_options,这听起来很有趣。我们输入它,然后maybe_default_option 到达一个大开关:

switch (default_opt->levels)
  

  [...]

  case OPT_LEVELS_1_PLUS:
    enabled = (level >= 1);
    break;

  [...]

  case OPT_LEVELS_3_PLUS:
    enabled = (level >= 3);
    break;

没有&gt;= 4 检查,这表明3 是最大的可能。

然后我们在common-target.h中搜索OPT_LEVELS_3_PLUS的定义:

enum opt_levels

  OPT_LEVELS_NONE, /* No levels (mark end of array).  */
  OPT_LEVELS_ALL, /* All levels (used by targets to disable options
                     enabled in target-independent code).  */
  OPT_LEVELS_0_ONLY, /* -O0 only.  */
  OPT_LEVELS_1_PLUS, /* -O1 and above, including -Os and -Og.  */
  OPT_LEVELS_1_PLUS_SPEED_ONLY, /* -O1 and above, but not -Os or -Og.  */
  OPT_LEVELS_1_PLUS_NOT_DEBUG, /* -O1 and above, but not -Og.  */
  OPT_LEVELS_2_PLUS, /* -O2 and above, including -Os.  */
  OPT_LEVELS_2_PLUS_SPEED_ONLY, /* -O2 and above, but not -Os or -Og.  */
  OPT_LEVELS_3_PLUS, /* -O3 and above.  */
  OPT_LEVELS_3_PLUS_AND_SIZE, /* -O3 and above and -Os.  */
  OPT_LEVELS_SIZE, /* -Os only.  */
  OPT_LEVELS_FAST /* -Ofast only.  */
;

哈!这是一个强有力的指标,表明只有 3 个级别。

opts.c:default_options_table

opt_levels 太有趣了,我们 grep OPT_LEVELS_3_PLUS,然后遇到opts.c:default_options_table

static const struct default_options default_options_table[] = 
    /* -O1 optimizations.  */
     OPT_LEVELS_1_PLUS, OPT_fdefer_pop, NULL, 1 ,
    [...]

    /* -O3 optimizations.  */
     OPT_LEVELS_3_PLUS, OPT_ftree_loop_distribute_patterns, NULL, 1 ,
    [...]

所以这是 -On 到文档中提到的特定优化映射的编码位置。不错!

确保不再使用 x_optimize

x_optimize 的主要用途是设置其他特定的优化选项,如手册页中所述的-fdefer_pop。还有吗?

我们grep,并找到更多。数量很少,人工检查发现每次使用最多只做一个x_optimize &gt;= 3,所以我们的结论成立。

lto-wrapper.c

现在我们寻找OPT_O 的第二次出现,它在lto-wrapper.c 中。

LTO 表示链接时间优化,顾名思义,它需要一个-O 选项,并将链接到collec2(基本上是一个链接器)。

其实lto-wrapper.c的第一行说:

/* Wrapper to call lto.  Used by collect2 and the linker plugin.

在这个文件中,OPT_O 的出现似乎只是规范了 O 的值以将其向前传递,所以我们应该没问题。

【讨论】:

回答的很详细,印象深刻!引擎盖下的 GCC。【参考方案2】:

老实说,有 8 种不同的有效 -O 选项可以提供给 gcc,尽管有些意思相同。

这个答案的原始版本说有 7 个选项。 GCC 此后添加了-Og,使总数达到 8 个

来自man page:

-O(同-O1-O0(不做优化,不指定优化级别则默认) -O1(最小优化) -O2(优化更多) -O3(进一步优化) -Ofast(非常积极地优化到违反标准的程度) -Og(优化调试体验。-Og启用不干扰调试的优化。应该是 标准编辑-编译-调试周期的优化级别选择,提供合理的优化级别 同时保持快速编译和良好的调试体验。) -Os(针对大小进行优化。-Os 启用通常不会增加代码大小的所有 -O2 优化。它还执行进一步的优化 旨在减少代码大小。 -Os 禁用以下优化标志:-falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays -ftree-vect-loop-version)

也可能有特定于平台的优化,正如@pauldoo 所说,OS X 有-Oz

【讨论】:

如果你在 Mac OS X 上开发,还有一个额外的 -Oz 设置,它是“比-Os更积极地优化大小”:developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/… 注意:O3 不一定比 O2 好,即使顾名思义。两者都试试。 @pauldoo 404 页面,替换为archive.org @pauldoo 工作链接gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Optimize-Options.html 调用“Os”优化大小是 IMO 误导,因为它仍然主要针对速度进行优化,但它只是跳过或更改某些可能导致代码大小增加的优化。您确实在您的文本中很好地解释了这一点,只是指出了我通常所说的一个烦恼,即“优化大小”意味着这与优化速度相反。永远不应该使用“O0”,因为它会生成类似于 1970 年代编译器的荒谬代码,并且现在“Og”存在,几乎所有使用它的剩余理由都消失了【参考方案3】:

七个不同的级别:

-O0(默认):无优化。

-O-O1(相同):优化,但不要花太多时间。

-O2:更积极地优化

-O3:优化最激进

-Ofast:等效于-O3 -ffast-math-ffast-math 触发不符合标准的浮点优化。这允许编译器假装浮点数是无限精确的,并且它们的代数遵循实数代数的标准规则。它还告诉编译器告诉硬件将非规范化刷新为零并将非规范化视为零,至少在某些处理器上,包括 x86 和 x86-64。非正规在许多 FPU 上触发慢速路径,因此将它们视为零(这不会触发慢速路径)可能会大大提高性能。

-Os:优化代码大小。由于更好的 I-cache 行为,这实际上可以在某些情况下提高速度。

-Og:优化,但不干扰调试。这为调试构建提供了不令人尴尬的性能,并旨在替换 -O0 进行调试构建。

还有其他选项未启用,必须单独启用。也可以使用优化选项,但禁用此优化启用的特定标志。

有关更多信息,请参阅 GCC 网站。

【讨论】:

确实,虽然为了公平对待其他答案,但在编写这些答案时,-Ofast 和 -Og 都不存在。 那么为什么-O100 会编译呢? @einpoklum 因为 GCC 将 -O3 以上的所有内容都视为 -O3。 不幸的是,您仍然在使用 -Og 的调试器中获得大量 。脚步仍然随机跳跃。恕我直言,这是没用的。【参考方案4】:

四 (0-3):参见 GCC 4.4.2 manual。任何更高的值都只是 -O3,但在某些时候你会超出可变大小限制。

【讨论】:

我已经探索了源代码in my answer,同意你的看法。更迂腐的是,GCC 似乎依赖于 atoi 未定义的行为,然后是 255 内部限制。 请考虑删除您的答案,因为它(至少这些天)不正确。

以上是关于GCC 优化级别有多少?的主要内容,如果未能解决你的问题,请参考以下文章

Clang 优化级别

gcc的常用编译命令

gcc的调试调研——gdb

查看控制编译优化的选项

查看控制编译优化的选项

GCC 优化在运行时导致“未定义的符号”