有多少个 GCC 优化级别?

有多少个 海湾合作委员会优化级别?

我试了 gcc-O1 gcc-O2 gcc-O3和 gcc-O4

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

但是,我试过了吗

gcc -O100

然后编译成。

有多少个优化级别?

83595 次浏览

4(0-3) : 参见海湾合作委员会4.4.2 手动操作。任何更高的只是-O3,但在某一点上,你会超出可变大小的限制。

迂腐地说,有8种不同的有效 -O 选项,你可以给 gcc,虽然有一些意味着同样的事情。

这个答案的原始版本声明有7个选项。海湾合作委员会已经增加了 -Og,使总数达到8。

来自 主页:

  • -O(与 -O1相同)
  • -O0(不进行优化,如果没有指定优化级别,则为默认值)
  • -O1(最小优化)
  • -O2(更多优化)
  • -O3(进一步优化)
  • -Ofast(非常积极地优化到打破标准一致性的程度)
  • 优化调试经验。- 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

七个不同的层次:

  • -O0(默认) : 没有优化。

  • -O-O1(同样的事情) : 优化,但不要花费太多的时间。

  • 更积极地优化

  • 最积极地优化

  • 相当于 -O3 -ffast-math-ffast-math触发不符合标准的浮点优化。这允许编译器假设浮点数是无限精确的,它们的代数遵循实数代数的标准规则。它还告诉编译器告诉硬件将异常值刷新为零,并将异常值视为零,至少在某些处理器上是这样,包括 x86和 x86-64。在许多 FPU 上,数规范会触发一条缓慢的路径,因此将它们视为零(这并不会触发缓慢的路径)可能是一个巨大的性能胜利。

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

  • -Og: 优化,但不要干扰调试。这为调试构建提供了非尴尬的性能,并用于替换调试构建的 -O0

还有其他一些选项没有被这些选项中的任何一个选项启用,必须单独启用。也可以使用优化选项,但禁用此优化所启用的特定标志。

有关详细信息,请参阅海湾合作委员会网站。

让我们来解释 GCC 5.1的源代码

我们将试图理解在 -O100上发生了什么,因为它在手册页上并不清楚。

我们的结论是:

  • 任何超过 -O3INT_MAX的数据都与 -O3相同,但这在将来很容易改变,所以不要依赖它。
  • 如果输入大于 INT_MAX的整数,gcc5.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

哦,共同点,选吧

Opt 内部文档中描述的特定于 GCC 的 CLI 选项描述格式,由 光学生光学元件转换为 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<n>是如何从其他 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(双破折号,因为它以一个破折号 -optimize开始在 .opt文件)是一个未记录的别名 -O,可以用作 --optimize=3

使用 OPT _ O 的位置

现在我们要做的是:

git grep -E '\bOPT_O\b'

这让我们找到了两个文件:

我们先追踪 opts.c

C: default _ options _ Optimation

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

我们通过 grep 回溯查看谁调用了这个函数,我们发现唯一的代码路径是:

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

main.ccc1的入口,很好!

这个函数的第一部分:

  • OPT_O对应的字符串调用 atoi来解析输入参数
  • 将值存储在 opts->x_optimize中,其中 optsstruct gcc_opts

结构 gcc _ opts

在徒劳地摸索之后,我们注意到这个 struct也是在 options.h产生的:

struct gcc_options {
int x_optimize;
[...]
}

x_optimize从哪里来:

Variable
int optimize

以及 options.c:

struct gcc_options global_options;

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

255是内部最大值

opts.c:integral_argument中,atoi应用于输入参数,因此 INT_MAX是上界。如果你把任何更大的,似乎海湾合作委员会运行 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实际上是内部最大值。

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;

没有 >= 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个水平。

C: default _ options _ table

opt_levels是如此有趣,我们抓住 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 _ Optimization.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x

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

我们 grep再找几个。这个数字很小,经过手工检查,我们发现每种用法最多只能做到 x_optimize >= 3,所以我们的结论是正确的。

包装纸

现在我们来看看 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的值以便将其传递,因此应该没有问题。