歡迎光臨
每天分享高質量文章

宋寶華: 關於Linux編譯最佳化幾個必須掌握的姿勢

01

編譯選項和核心編譯

首先我們都知道,Linux核心如果用O0編譯,是無法編譯過的,Linux的核心編譯,要麼是O2,要麼是Os,這點從Linux的Makefile裡面可以看出:

當選擇了

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 

發現差異小的可憐:

O2和Os都使能了inline small函式和called once的函式,但是O2裡面-finline-functions是關閉的,而Os裡面是開的。O2裡面optimize-strlen是開的,Os裡面這個選項是關閉的。相關選項的含義可以透過”man gcc”看出(有問題,找男人),譬如man gcc後檢索inline-functions:

從O0到O1,O2,O3,是一個開啟的最佳化選項逐步加大的過程:

kernel用O0編譯不過,是因為kernel本身也沒有想用O0能夠編譯過,它的設計裡麵包含了編譯會最佳化的假想。下麵我們用一個簡單的例子來說明。

02

一個簡單的例子

下麵的程式碼:

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()沒有定義也沒有關係。

把程式碼稍微改一下後:

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就過不了。核心裡面有許多類似設想編譯器會進行最佳化的程式碼。

3.我們不想inline了

由於編譯的最佳化,有些函式(比如小函式和全工程裡面只被一個人呼叫的函式)雖然沒有顯示地寫成inline,但是編譯器最佳化為inline了,這給除錯造成了一些麻煩,因為找不到這個函式對應的symbol了。

這個時候,我們可以顯示地寫明某些函式我們不想inline:

否則,上面2個函式,即便你程式碼裡面沒有寫inline,由於O2和Os使能了相關的inline選項,也可能被編譯器自動inline掉,如果我們想拒絕inline,可以透過noline來標識。

4.我不想被最佳化

在全域性已經使能O1, O2, O3, Os的情況下,某個單獨的函式我們不想做任何的最佳化,可以用__attribute__((optimize(“O0”)))來修飾這個函式,比如我們把上述用O2可以編譯過的程式碼進行如下修改:

重新用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

5. 總結的話

下麵給幾條實踐指南:

  • 儘量不要嘗試用O0去編譯核心,這不符合真實的工程實踐,也不太被主流Linux社群所支援;核心依賴O2/Os去做較多的最佳化;

  • 追求你的程式碼在O2的情況下,仍然是正確的,程式碼要經得起編譯最佳化;比如O0工作正常,而O2不正常,應該盡可能從自身找原因,分析彙編;

  • 如果在全域性最佳化的情況下,想針對某個區域性避免最佳化,可以嘗試用noinline__attribute__((optimize(“O0”)))等進行外科手術式地調整。

贊(0)

分享創造快樂