Mac OS/Android在下面Static Initializer


Mozillaproject师通过优化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的优化


在Mozillaproject师的文章[链接]中,基于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: 应用程序启动速度的优化_数据_02


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: 应用程序启动速度的优化_初始化_03

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例如以下: 应用程序启动速度的优化_android_04

这是一个晦涩的主题,非党值得深入研究。能够从作者提供的链接入手展开。我水平有限。抛砖引玉,期待着更为深入的阐述。


參考

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​