应用程序启动速度优化

简介: Mac OS/Android下的Static Initializer Mozilla工程师通过优化Static Initializer(静态初始化,或全局建构函数, Global Constructor)和Binary布局来提升FireFox启动速度的文章,非常有参考价值。

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

目录
相关文章
|
缓存 算法 NoSQL
如何优化Java应用程序的性能
无论是开发大型企业应用程序还是小型工具,Java一直是一个受欢迎的编程语言。然而,随着应用程序规模的增长和用户需求的变化,性能成为了一个关键问题。本篇博客将介绍一些优化Java应用程序性能的方法。
126 1
|
4月前
|
Prometheus 监控 Cloud Native
如何优化Java应用的内存使用
如何优化Java应用的内存使用
|
4月前
|
存储 设计模式 监控
运用Unity Profiler定位内存泄漏并实施对象池管理优化内存使用
【7月更文第10天】在Unity游戏开发中,内存管理是至关重要的一个环节。内存泄漏不仅会导致游戏运行缓慢、卡顿,严重时甚至会引发崩溃。Unity Profiler作为一个强大的性能分析工具,能够帮助开发者深入理解应用程序的内存使用情况,从而定位并解决内存泄漏问题。同时,通过实施对象池管理策略,可以显著优化内存使用,提高游戏性能。本文将结合代码示例,详细介绍如何利用Unity Profiler定位内存泄漏,并实施对象池来优化内存使用。
238 0
|
4月前
|
监控 算法 Java
如何优化Java应用程序的内存管理
如何优化Java应用程序的内存管理
|
5月前
|
存储 缓存 Java
JVM的即时编译(JIT)优化原理:加速程序的执行
JVM的即时编译(JIT)优化原理:加速程序的执行
|
5月前
|
Java 编译器
全面解析JVM加载中初始化的时机
全面解析JVM加载中初始化的时机
|
存储 Java 编译器
JVM学习日志(六) JVM从加载到内存全过程
JVM从加载到内存全过程 简述
84 0
JVM学习日志(六) JVM从加载到内存全过程
|
Java
JVM由那些部分组成,运行流程是什么?
JVM由那些部分组成,运行流程是什么?
91 0
使用Lightrun对Java应用程序进行性能调整
简介 在这篇文章中,我将向你展示使用Lightrun分析一个Java应用程序,这样你就可以发现各种性能调整的改进,你可以应用到你当前的Java应用程序。 在上一篇文章中,我解释了什么是Lightrun,以及你如何使用它来注入动态日志、捕获运行时快照或添加动态指标。 在这篇文章中,我将使用Lightrun作为我的JPA关联获取验证器的替代品。
|
小程序 前端开发
小程序启动参数相关问题
小程序启动参数相关问题
367 0