7

关于Linux编译优化几个必须掌握的姿势

 3 years ago
source link: https://club.perfma.com/article/2048537
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
coffeeboy
linux
1周前

编译选项和内核编译

首先我们都知道,Linux内核如果用O0编译,是无法编译过的,Linux的内核编译,要么是O2,要么是Os,这点从Linux的Makefile里面可以看出:

image.png
CONFIG_CC_OPTIMIZE_FOR_SIZE

它会是Os,否则就是O2。

其实O2和Os,都是一些优化选项的集合:

gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts

gcc -c -Q -Os --help=optimizers > /tmp/Os-opts

前者倾向于基于速度的优化,后者倾向于基于size更小的优化。对比二者的开关选项:

meld /tmp/O2-opts /tmp/Os-opts 

发现差异小的可怜:

image.png

O2和Os都使能了inline small函数和called once的函数,但是O2里面-finline-functions是关闭的,而Os里面是开的。O2里面optimize-strlen是开的,Os里面这个选项是关闭的。相关选项的含义可以通过"man gcc"看出(有问题,找男人),譬如man gcc后检索inline-functions:

image.png

从O0到O1,O2,O3,是一个开启的优化选项逐步加大的过程:

image.png

kernel用O0编译不过,是因为kernel本身也没有想用O0能够编译过,它的设计里面包含了编译会优化的假想。下面我们用一个简单的例子来说明。

一个简单的例子

下面的代码:

image.png

O0编译会报如下错,说f()函数没有定义:

$ gcc -O0 cc.c

cc.c:1:13: warning: ‘f’ used but never defined [enabled by default]

 void f(void);

             ^

/tmp/ccTwwtHG.o: In function `main':

cc.c:(.text+0x19): undefined reference to `f'

collect2: error: ld returned 1 exit status

但是用O2编译,则没有问题:

$ gcc -O2 cc.c

原因在于,O2编译,它意识到a==1,所以if(a>2),它不会成立,所以f()没有定义也没有关系。

把代码稍微改一下后:

image.png

O2这个时候也不行了:

$ gcc -O2 cc.c

/tmp/ccXiyBHn.o: In function `main':

cc.c:(.text.startup+0x7): undefined reference to `f'

collect2: error: ld returned 1 exit status

所以,通过这个例子,大家可以看出来为什么同样的代码,用O2就可以过,用O0就过不了。内核里面有许多类似设想编译器会进行优化的代码。

我们不想inline了

由于编译的优化,有些函数(比如小函数和全工程里面只被一个人调用的函数)虽然没有显示地写成inline,但是编译器优化为inline了,这给调试造成了一些麻烦,因为找不到这个函数对应的symbol了。

这个时候,我们可以显示地写明某些函数我们不想inline:

image.png

否则,上面2个函数,即便你代码里面没有写inline,由于O2和Os使能了相关的inline选项,也可能被编译器自动inline掉,如果我们想拒绝inline,可以通过noline来标识。

我不想被优化

在全局已经使能O1, O2, O3, Os的情况下,某个单独的函数我们不想做任何的优化,可以用__attribute__((optimize(“O0”)))来修饰这个函数,比如我们把上述用O2可以编译过的代码进行如下修改:

image.png

重新用O2编译:

$ gcc -O2 cc.c

/tmp/cc8M338p.o: In function `main':

cc.c:(.text+0x19): undefined reference to `f'

collect2: error: ld returned 1 exit status

下面给几条实践指南:

  • 尽量不要尝试用O0去编译内核,这不符合真实的工程实践,也不太被主流Linux社区所支持;内核依赖O2/Os去做较多的优化;

  • 追求你的代码在O2的情况下,仍然是正确的,代码要经得起编译优化;比如O0工作正常,而O2不正常,应该尽可能从自身找原因,分析汇编;

  • 如果在全局优化的情况下,想针对某个局部避免优化,可以尝试用noinline,attribute((optimize(“O0”)))等进行外科手术式地调整。

本文来自公众号:Linux阅码场,作者:宋宝华


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK