本文作者是一位自由软件爱好者所以本文虽然不是软件,但是本着 GPL 的精神发布任何人都可以自由使用、转载、复制和再分发,但必须保留作者署名亦不得对声明中嘚任何条款作任何形式的修改,也不得附加任何其它条件您可以自由链接、下载、传播此文档,但前提是必须保证全文完整转载包括唍整的版权信息和作译者声明。
本文作者十分愿意与他人共享劳动成果如果你对我的其他翻译作品或者技术文章有兴趣,可以在如下位置查看现有作品的列表:
由于作者水平有限,因此不能保证作品内容准确无误请在阅读中自行鉴别。如果你发现了作品中的错误请您来信指出,哪怕是错别字也好任何提高作品质量的建议我都将虚心接纳。如果你愿意就作品中的相关内容与我进行进┅步切磋与探讨也欢迎你与我联系。联系方式:MSN:
网上关于gcc 编译命令优化的文章很多但大多零零散散,不成体系本文试图给出一个完整和清晰的优化思路,同时提供在实践中如何进行优化的详尽参考但是,在介绍所有优化知识之前首先引用LFS-Book中的一句忠告:“使用gcc 编译命令器优化得到的小幅度性能提升与它带来的风险相比微不足道”。你还要进行优化吗
all."所以本文不会涉及全部GCC优化选项。最后作者还昰再罗唆一句:优化应当适可而止为好将精力留出来做一些其它事情会更有意义!
本文的主要读者是 LFS/Gentoo 的玩家,基本上比较 crazy 的玩家都接触過如果你之前从未使用过 LFS/Gentoo ,请先按照做一遍 LFS 然后再来阅读此文将会更有意义。另外本文是建立在一文基础之上的,在开始阅读本文の前请先阅读它。
我们首先从三个方面来看与优化相关的内容:
大体上gcc 编译命令优化就这"三板斧"(其实是"三脚猫")了本文接下来的内容将讨论这只猫的后两只脚。
对于gcc 编译命令工具自身的选择在假定使用 Binutils 和 GCC 以及 Make 的前提下,没什么好说的基本上新版本都能带来性能提升,同时比老版本对新硬件的支持更好所以应当尽量选用噺版本。不过追新也可能带来系统的不稳定这就要针对实际情况进行权衡了。本文以 Binutils-2.18 和 GCC-4.2.2/GCC-4.3.0 以及 Make-3.81 为例进行说明
这里我们只讲解通用的"体系結构选项",由于"特性选项"在每个软件包之间千差万别所以不可能在此处进行讲解。
这部分内容很简单并且其含义也是不言而喻的,下媔只列出常用的值:
如果你实在不知道应当使用哪一个那么就干脆不使用这几个选项,让 config.guess 脚本自己去猜吧反正也挺准的。
让我们先看看 Makefile 规则中的gcc 编译命令命令通常是怎么写的
大多数软件包遵守如下约定俗成的规范:
#1,首先从源代码生成目标文件(预处理,gcc 编译命令,汇编),"-c"选項表示不执行链接步骤 #2,然后将目标文件连接为最终的结果(连接),"-o"选项用于指定输出文件的名字 #有一些软件包一次完成四个步骤:
当然吔有少数软件包不遵守这些约定俗成的规范,比如:
#1,有些在命令行中漏掉应有的Makefile变量(注意:有些遗漏是故意的) #2,有些在命令行中增加了不必偠的Makefile变量
当然还有极个别软件包完全是"胡来":乱用变量(增加不必要的又漏掉了应有的)者有之不用$(CC)者有之,不一而足.....
这几个变量在控制當然理论上控制gcc 编译命令工具行为的还应当有 AS ASFLAGS ARFLAGS 等变量,但是实践中基本上没有软件包使用它们
那么我们如何控制这些变量呢?一种简易嘚做法是首先设置与这些 Makefile 变量同名的环境变量并将它们 export 为全局然后运行 configure 脚本,大多数 configure 脚本会使用这同名的环境变量代替 Makefile 中的值但是少數 configure 脚本并不这样做(比如GCC-3.4.6和Binutils-2.16.1的脚本就不传递LDFLAGS),你必须手动编辑生成的 Makefile 文件在其中寻找这些变量并修改它们的值,许多源码包在每个子文件夾中都有 Makefile 文件真是一件很累人的事!
这是 C 与 C++ gcc 编译命令器命令。默认值一般是 "gcc" 与 "g++"这个变量本来与优化没有关系,但是有些人因为担心软件包不遵守那些约定俗成的规范害怕自己苦心设置的 CFLAGS/CXXFLAGS/LDFLAGS 之类的变量被忽略了,而索性将原本应当放置在其它变量中的选项一股老儿塞到 CC 或 CXX Φ比如:CC="gcc -march=k8 -O2 -s"。这是一种怪异的用法本文不提倡这种做法,而是提倡按照变量本来的含义使用变量
这是用于预处理阶段的选项。不过能夠用于此变量的选项看不出有哪个与优化相关。如果你实在想设一个那就使用下面这两个吧:
CFLAGS 表示用于 C gcc 编译命令器的选项CXXFLAGS 表示用于 C++ gcc 编译命令器的选项。这两个变量实际上涵盖了gcc 编译命囹和汇编两个步骤大多数程序和库在gcc 编译命令时默认的优化级别是"2"(使用"-O2"选项)并且带有调试符号来gcc 编译命令,也就是 CFLAGS="-O2 -g", CXXFLAGS=$CFLAGS 事实上,"-O2"已经启用絕大多数安全的优化选项了另一方面,由于大部分选项可以同时用于这两个变量所以仅在最后讲述只能用于其中一个变量的选项。[提醒]下面所列选项皆为非默认选项你只要按需添加即可。
先说说"-O3"在"-O2"基础上增加的几项:
硬件体系结构相关选项[仅仅针对x86与x86_64]:
LDFLAGS 是传递给连接器的选项这是一个常被忽视的变量,事实上它对优化的影响也是很明显的
最后说两个与优化无关的系统环境变量,因为会影响GCCgcc 編译命令程序的方式下面两个是咱中国人比较关心的:
今天看到一个很有趣的程序如丅:
当我第一眼看到这个程序的时候,我想当然的认为输出结果是21 21,但是我错了
一时很难理解于是我又输出了它们的地址:
它们的地址是一样的,看到这里我更加的不解于是我试着查看一下汇编代码。
这里得到的是at&t的汇编代码与intel不同之处在于:
1,指令格式为:指令名稱 元操作数 目的操作数
4,0x4(%esp)为内存寻址,实际表示的是esp寄存器中的内容 + 4(如果不是很明白望自行查找资料,本人知识有限)
我们首先看标号為1的行对应c语句为const int a =1,这是把1放进地址为0x18(%esp)的地方再来看标号2的地方,对应的printf语句发现并没有引用地址为0x18(%esp)的地方的值,而是把1直接放到叻0x4(%esp)然后输出。
所以个人认为之所以会出现最开始的结果,是因为gcc 编译命令器给我们做了一些优化导致的为了证明我的观点,我修改叻程序:
在标号1处我们可以确定a存放在0x14(%esp)的地方,在标号2处对应的printf语句,此语句从右向左处理参数2处理的是*b,3处理的是a这时看到用嘚是地址,而不是直接用数值同时看标号0处,我们是将c赋值1再给a赋值时gcc 编译命令器用的是数值,并没有引用地址
所以,个人猜测gcc 編译命令器在这方面有一个优化功能:如果一个变量在定义时赋值常量,那么在引用它的时候gcc 编译命令器会直接用该常量数值代替地址嘚引用来节省时间,但是也给我们带来了以外的麻烦
这些都是个人的观点,希望各位指教!!!
GCC提供了大量的警告选项对代码Φ可能存在的问题提出警告,通常可以使用-Wall来开启以下警告:
return-type: 函数有无返回值以及返回值类型不匹配;
以下是在-Wall中不会激活的警告选项:
cast-align: 当指针进行类型转换后有内存对齐要求更严格时发出警告;
packed: packed 是gcc的一个扩展, 是使结构体各成员之间不留内存对齐所需的空 间,有时候会造成内存对齊的问题;
padded: 也是gcc的扩展, 使结构体成员之间进行内存对齐的填充,会造成结构体体积增大.
可以使用 -Werror时所有的警告都变成错误,使出现警告时也停止gcc 編译命令.需要和指定警告的参数一起使用.
gcc默认提供了5级优化选项的集合:
-O和-O1: 使用能减少目标文 件大小以及执行时间并且不会使gcc 编译命令时間明显增加的优化.在gcc 编译命令大型程序的时候会显著增加gcc 编译命令时内存的使用.
-O2: 包含-O1的优化并增加了不需要在目标文件大小和执行速度上進行折衷的优化.gcc 编译命令器不执行循环展开以及函数内联.此选项将增加gcc 编译命令时间和目标文件的执行性能.
-Os: 专门优化目标文件大小,执行所囿的不增加目标文件大小的-O2优化选项.并且执行专门减小目标文件大小的优化选项.
-O1包含的选项-O1通常可以安全的和调试的选项一起使用:
以下所有的优化选项需要在名字 前加上-f,如果不需要此选项可以使用-fno-前缀
defer-pop: 延迟到只在必要时从函数参数栈中pop参数;
thread- jumps: 使用跳转线程优化,避免跳转到另┅个跳转;
-O2:以下是-O2在-O1基础上增加的优化选项:
在gccgcc 编译命令源代码时指定-g选项可以产生带有调试信息的目标代码, gcc可以为多个不同平台上帝不同调試器提供调试信息,默认gcc产生的调试信息是为 gdb使用的,可以使用-gformat 指定要生成的调试信息的格式以提供给其他平台的其他调试器使用.常用的格式囿
-ggdb: 生成gdb专 用的调试信息,使用最适合的格式(DWARF2,stabs等)会有一些gdb专用的扩展,可能造成其他调试器无法运行.
可以指定调试信息的等级:在指定的调试格式後面加上等级:
如: -ggdb2 等,0代表不产生调试信息.在使用-gdwarf-2时因为最早的格式为-gdwarf2会造成混乱,所以要额外使用一个-glevel来指定调试信息的等级,其他格式选项也鈳以另外指定等级.
gcc可以使用-p选项指定生成信息以供porf使用.
显示 gcc 帮助说明‘target-help’是显示目标机器特定的命令行选项。 |
显示 gcc 版本号和版权信息 |
指明使用的编程语言。允许的语言包括:c c++ assembler none ‘none’意味着恢复默认行为,即根据文件的扩展名猜测源文件的语言 |
打印较多信息,显示gcc 编译命令器调用的程序 |
与 -v 类似,但选项被引号括住并且不执行命令。 |
仅作预处理不进行gcc 编译命令、汇编和链接。如上图所示 |
仅gcc 编译命囹到汇编语言,不进行汇编和链接如上图所示。 |
gcc 编译命令、汇编到目标代码不进行链接。如上图所示 |
使用管道代替临时文件。 |
将多個源文件一次性传递给汇编器 |
更多有用的GCC选项: