應用程式啟動速度最佳化
Mac OS/Android下的Static Initializer
Mozilla工程師通過最佳化Static Initializer(靜態初始化,或全域建構函數, Global Constructor)和Binary布局來提升FireFox啟動速度的文章,非常有參考價值。文章中以x86及x86-64平台為基礎,下面加了Mac OS及Android上的binary布局。
什麼是Static Initializer? 簡而言之就是全域C++對象的初始化。有人笑稱一個C++程式的main()函數執行之前,可能該做事都做完了,這就是Static Initializer的影響。如果裡面又有一層層依賴引用,就會大大影響啟動時間。下面是一個樣本程式:
MyClass oneClass(0x010203);const MyClass twoClass(0x010204);attribute ((constructor)) void foo(void){ printf(“foo is running and printf is available at this point\n”);}int main(int argc, const char * argv[]){ //do something here…}
前兩個對象oneClass和twoClass即是使用了靜態初始化的兩個對象, 而foo函數則通過編譯選項強制放到程式的初始化段(init segement)中,在程式初始化時就會執行。以下即最終在Mac OS上的布局:在Android ARM ELF中則是下面這個布局:
FireFox的最佳化在Mozilla工程師的文章[連結]中,基於Firefox 4.0b8在x86及x86-64的測試資料發現如下的平均啟動時間:
|
平均啟動時間(ms) |
Pages Read |
Bytes Read |
| x86 |
3,228.76 ± 0.57% |
4,787 |
19,607,552 |
| x86-64 |
3,382.0 ± 0.51% |
5,874 |
24,059,904 |
使用systemtap[連結]可以得到一個訪問核心庫libxul.so的access pattern:
Static Initializers
在開始時那些垂直的線段正是Static Initializers啟動並執行時間,佔去了不少的時間。解決之道就是減少static initializers,特別留心那些全域變數、靜態變數。
以這種方法分析了一下,一共有237個static initializers,其中147是由cycle collection globals所引入的。經過修正後cycle collection的全域對象降到一個,整個情況並未有大的改觀:
|
平均啟動時間(ms) |
Pages Read |
Bytes Read |
| x86 |
3,216.1 ± 0.59% |
4,656 |
19,070,976 |
| x86-64 |
3,488.14 ± 0.75% |
5,759 |
23,588,864 |
以下是新的I/O access pattern:
I/O雖然有所降低,其實還有許多其它內容的讀操作在static initialization前已經發生了,所以還有別的工作需要做。
Reordering objects
另一工作即是重新布局binary, 讓核心需要的資料可以儘快擷取。之前Taras的一個研究發現只要做些toolchain上的變更就可以實現。
使用Taras的icegrind做了最佳化後,改進變得明顯了:
|
平均啟動時間(ms) |
Pages Read |
Bytes Read |
| x86 |
2,939.18 ± 0.81% |
4,129 |
16,912,384 |
| x86-64 |
3,247.64 ± 0.68% |
5,254 |
21,520,384 |
I/O pattern:
Packing Relocations
最後,再可以通過減少relocation段,來最佳化啟動時間。這樣可以有效降低I/O,以及dynamic relocations section,也能減小程式包。我使用的工具在這裡。參考:關於通過調整ELF最佳化啟動時間下面是最終的效果:
|
平均啟動時間(ms) |
Pages Read |
Bytes Read |
| x86 |
3,149.32 ± 0.62% |
4,443 |
18,198,528 |
| x86-64 |
3,191.58 ± 0.62% |
4,733 |
19,386,368 |
I/O Pattern如下:
這是一個晦澀的主題,非黨值得深入研究,可以從作者提供的連結入手展開。我水平有限,拋磚引玉,期待著更為深入的闡述。
轉載請註明出處: http://blog.csdn.net/horkychen
參考
1. How to Make Startup Suck Less (Also Reduce Memory Usage!)
2. Death by static initialization
3. icegrind - Valgrind Plugin for optimizing Cold Startup
4. Resolving ELF Relocation Name / Symbols
5. Static initializers
6. ELF for ARM Architecture