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:

  1. 红点表示从磁盘加载的页数
  2. 红线则是在文件中的定位(seek)操作
  3. 背景中的色块代表了.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

更多相关文章

  1. Android中的Application类
  2. 消除 activity 启动时白屏、黑屏问题
  3. beagleboard 启动 android 内核
  4. android简易倒计时器
  5. android之在启动运用程序的时候彻底隐藏TitleBar
  6. 深入理解ActivityManagerService,你知道的不知道的全在这里!
  7. Android(安卓)获取系统时间
  8. Android(安卓)Intent和PendingIntent的区别详细说明
  9. Android分页中显示出下面翻页的导航栏的布局实例代码

随机推荐

  1. CCNA最实用的复习知识点(4)
  2. CCNA最实用的复习知识点(5)
  3. CVE-2019-12922:phpMyAdmin 0 Day漏洞
  4. Session会话与Cookie简单说明
  5. Linux基础课程汇总-辛舒展-专题视频课程
  6. 程序员如何用技术变现(上)
  7. 从运维角度看中大型网站架构的演变之路
  8. 总结几个常用的系统安全设置(含DenyHosts)
  9. Linux 磁盘分区管理
  10. 在高并发、高负载的情况下,如何给表添加字