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:
红点表示从磁盘加载的页数
红线则是在文件中的定位(seek)操作
背景中的色块代表了.rel.dyn/.rela.syn(红色),.text(粉色),.rodata(绿色),.data.rel.ro(淡绿色)。
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