有多少个GCC优化级别?
我尝试了gcc -O1、gcc -O2、gcc -O3和gcc -O4。
如果我使用一个非常大的数字,它就不起作用。
但是,我已经尝试过
gcc -O100
并且它被编译了。
有多少个优化级别?
有多少个GCC优化级别?
我尝试了gcc -O1、gcc -O2、gcc -O3和gcc -O4。
如果我使用一个非常大的数字,它就不起作用。
但是,我已经尝试过
gcc -O100
并且它被编译了。
有多少个优化级别?
严谨来说,您可以给gcc提供8种不同的有效-O选项,尽管有些选项具有相同的含义。
这个答案的原始版本声称只有7个选项。GCC后来添加了-Og
,使总数达到8个。
来源于man页面:
-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
。
-Oz
设置,它比 -Os
更积极地优化尺寸:http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Optimize-Options.html - pauldoo让我们解释GCC 5.1的源代码
我们将尝试理解在-O100
上发生了什么,因为在手册上不太清楚。
我们得出结论:
-O3
到INT_MAX
以上的任何内容与-O3
相同,但未来可能会轻松更改,因此不要依赖它。INT_MAX
的整数,则GCC 5.1会运行未定义的行为。-O-1
关注子程序
首先请记住,GCC只是cpp
,as
,cc1
,collect2
的前端。快速运行 ./XXX --help
表明只有collect2
和cc1
采用 -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
参数被转发到了cc1
和collect2
。
common.opt中的-O选项
common.opt 是一种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<n>
与其他Os
、Ofast
和Og
不属于同一族群。
构建时,会生成一个包含以下内容的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
开始,所以使用双破折号)是 -O
的未记录别名,并且可以使用 --optimize=3
!
OPT_O 的使用
现在我们使用 grep:
git grep -E '\bOPT_O\b'
这指向两个文件:
首先我们来查找opts.c
opts.c:default_options_optimization
opts.c
的所有用法都在default_options_optimization
函数里。
我们使用grep回溯调用此函数的代码,我们发现唯一的代码路径是:
main.c:main
toplev.c:toplev::main
opts-global.c:decode_opts
opts.c:default_options_optimization
main.c
是cc1
的入口点。很好!
此函数的第一部分:
integral_argument
,它对与 OPT_O
相应的字符串调用 atoi
来解析输入参数opts->x_optimize
中,其中 opts
是一个 struct gcc_opts
。struct gcc_opts
在枯燥的搜索中,我们注意到该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;
没有>= 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. */
};
哈!这是只有三个级别的一个强烈指示。
opts.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_optimize
x_optimize
的主要用途是设置其他特定优化选项,如man页上所述的-fdefer_pop
。还有其他用途吗?
我们使用grep
并发现还有几个用法。数目很少,通过手动检查,我们看到每个使用只做了最多x_optimize >= 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
的值规范化以便向前传递,所以我们应该没问题。
共有七个不同的级别:
-O0
(默认值):没有优化。
-O
或 -O1
(相同):进行优化,但不花费太多时间。
-O2
:更积极地进行优化。
-O3
:最积极地进行优化。
-Ofast
:等同于 -O3 -ffast-math
。 -ffast-math
触发非标准兼容的浮点数优化。这使编译器可以假装浮点数是无限精确的,并且代数运算遵循实数代数的标准规则。它还告诉编译器告诉硬件将非规格化数值强制处理为零,并将非规格化数值视为零,至少在一些处理器上,包括 x86 和 x86-64。非规格化数值会触发许多 FPUs 上的慢速路径,因此将其视为零(不会触发慢速路径)可以大大提高性能。
-Os
:优化代码大小。这实际上在某些情况下可以提高速度,因为它可以改善指令高速缓存行为。
-Og
:进行优化,但不干扰调试。这使得调试构建具有非尴尬的性能,旨在取代 -O0
用于调试构建。
还有其他选项没有被任何一个选项启用,并且必须单独启用。也可以使用优化选项,但禁用这种优化启用的特定标志。
有关更多信息,请参见GCC网站。
-O100
可以编译呢? - einpoklum
man gcc
(大约 12000 行),你也可以搜索-O
,找到以下回答中提到的所有内容,以及更多的内容。 - Jens3
的数都与3
相同(只要它没有int
溢出)。请参见我的答案。 - Ciro Santilli OurBigBook.com-fomit-stack-pointer
将更改生成的代码。 - Basile Starynkevitch