编译简介(四)

本文简单介绍gcc的优化(-O)和调试(-g)选项。

遥想当年,同学教我用Visual Studio,开发选Debug,发布选Release,我还不知其所以然。工作后也曾以此作为面试题目,不少同学跟我当初一样青涩,回答起来支支吾吾。

其实没有纯粹的Debug和Release,都是不同选项的组合,以达到不同的目的。

使用-g选项编译,使得我们可以在源代码级别调试编译出来的程序,这也是我们通常意义下所说的调试,如调试C/C++程序。因此,我们一定会在开发时使用-g选项。为了获得更好的调试效果,通常我们不会打开编译器优化功能,即不使用-O选项(或者使用-O0显式的关闭优化)。如

gcc -g -O0 hello.c -o hello

或者

gcc -g hello.c -o hello

然而,-g-O不是互斥的。事实上,大多数Linux发行版的程序都使用了-g选项,这意味着,尽管调试效果不好,我们能够调试已经发布的程序(当然,如果真得打算编译的话,可能还需要安装额外的源代码包和调试信息包)。比如我们也可以这样编译:

gcc -g -O2 hello.c -o hello

另外,-g-O后面都可以接一个数字,一般地讲,数字越大,能力越强。比如优化能力

-O1 < -O2 < -O3

当然特定程序可能出现-O3的优化效果不如-O2的情况。由于-O3的某些优化过于激进,可能导致生成错误的程序,因此,大多数软件在发布时使用-O2作为默认优化选项。

-g是我们绝大多数时候会使用的编译选项,如果想调试宏,需要使用-g3,不过要小心,这可能导致生成非常大的文件。

编译简介(三)

在 什么是native compiler?什么是cross compiler? 一文中简单介绍了target的概念。就目前来讲,我们还不得不为每一个target单独编译一套SDK,暂时gcc还无法使用命令行来选择生成不同target的指令。

不过,在同样的target下,gcc提供了-march=, -mcpu=-mtune= 三个选项,生成相对于该硬件平台来说较高效的代码。

-march=用来指定目标体系结构,gcc通过该选项确定使用何种指令集,如对于ARM来说可以选择armv5te或者armv7-a等。

gcc -march=armv5te -O2 hello.c

gcc -march=armv7-a -O2 hello.c

-mcpu=用来指定目标处理器,相对于-march=来说,它提供的信息更加具体,gcc可以通过该选项判断使用何种指令集,它既可以代替-march=也可以一起使用,对于ARM来说可以选择iwmmxt或者cortex-a8等等。

gcc -mcpu=iwmmxt -O2 hello.c

gcc -mcpu=cortex-a8 -O2 hello.c

-mtune=不是用来指定指令集的,而是提供给gcc更多的信息,使其可以生成更加优化的代码,通常是用来选择流水线描述。由于即使是相同的指令集,仍然可能在微体系结构上有所不同,而流水线实现的不同,可能导致相互依赖的指令的执行时间有所不同。如果gcc得到相应的描述信息,则可以通过指令调度等优化算法,生成更优的程序。它需要和-march=或者-mcpu=一起使用。

那么在什么情况下该使用这些选项呢?

如果我们无法限定程序的运行环境,那么应该尽量选择更加通用的指令集。比如Linux发布可能生成i386的指令集,这样虽然无法发挥某些用户机器的强大能力,但是能够让更多的用户直接使用。于是,某些爱折腾的同学就会选择自己编译,甚至选择那些在本地编译的发布。

如果我们明确知道生成的程序运行在哪里,那么就应该提供尽可能具体的信息。比如android手机,通常手机厂商编译android时有明确的目标平台,那么就可以通过这几个选项获得最佳的性能。