Blog
Blog copied to clipboard
开机优化
Android系统开机优化设计到的地方比较多,从Frameworks,Kernel到Bootloader都需要改动。 后面慢慢总结更新
软硬件平台
硬件平台:iMX6DL双核 软件平台:Android4.4 Kernel3.0
调试工具
- logcat
- bootchart
- grabserial 可以方便抓取串口打印时机,对评估u-boot和内核启动时间很有作用。
sudo grabserial -v -d "/dev/ttyUSB0" -b 115200 -w 8 -p N -s 1 -e 30 -t
系统启动过程
首先需要对Android的启动过程要了解,一图胜千言。
第一阶段一般是芯片出厂的时候就固化了,无法优化。
第二阶段bootloader的优化主要是裁剪大小,去掉boot延时,这部分时机大概在500ms
[0.350919 0.000180] U-Boot 2009.08-01238-g590d62290e-dirty ( 5月 06 2019 - 10:44:17)
[0.356728 0.005809]
[0.356920 0.000192] CPU: Freescale i.MX6 family TO1.2 at 792 MHz
[0.360924 0.004004] Thermal sensor with ratio = 188
[0.373995 0.013071] Temperature: 48 C, calibration data 0x5a85027d
[0.378248 0.004253] mx6q pll1: 792MHz
[0.379900 0.001652] mx6q pll2: 528MHz
[0.381550 0.001650] mx6q pll3: 480MHz
[0.383185 0.001635] mx6q pll8: 50MHz
[0.384765 0.001580] ipg clock : 66000000Hz
[0.387169 0.002404] ipg per clock : 66000000Hz
[0.389642 0.002473] uart clock : 80000000Hz
[0.392048 0.002406] cspi clock : 60000000Hz
[0.394518 0.002470] ahb clock : 132000000Hz
[0.397038 0.002520] axi clock : 198000000Hz
[0.399367 0.002329] emi_slow clock: 99000000Hz
[0.401826 0.002459] ddr clock : 396000000Hz
[0.404370 0.002544] usdhc1 clock : 198000000Hz
[0.406861 0.002491] usdhc2 clock : 198000000Hz
[0.409383 0.002522] usdhc3 clock : 198000000Hz
[0.411907 0.002524] usdhc4 clock : 198000000Hz
[0.414439 0.002532] nfc clock : 24000000Hz
[0.416855 0.002416] Board: i.MX6DL/Solo-SABRESD: unknown-board Board: 0x61012 [POR ]
[0.422632 0.005777] Boot Device: MMC
[0.424185 0.001553] max16987_init
[0.425523 0.001338] DRAM: 1 GB
[0.429483 0.003960] MMC: FSL_USDHC: 0,FSL_USDHC: 1,FSL_USDHC: 2,FSL_USDHC: 3
[0.486995 0.057512] *** Warning - bad CRC or MMC, using default environment
[0.491839 0.004844]
[0.522670 0.030831] Error: no valid bmp image at 30000000
[0.531199 0.008529] In: serial
[0.532451 0.001252] Out: serial
[0.533737 0.001286] Err: serial
[0.535393 0.001656] Recovery key pressed
[0.537285 0.001892] Hit any key to stop autoboot: 0
U-boot的启动时间到Hit any key to stop autoboot就结束了。
内核优化
内核部分优化主要设计内核剪裁,内核压缩方式,关闭打印等。
内核剪裁
内核剪裁主要是把不需要的驱动去掉,这部分不同项目用到的驱动不同,这里不细说。
内核压缩
内核使用LZO
压缩方式比GIP
的方式镜像要大一些,但解压时间要快一些。但EMMC读取速度是非常快的大约几M每秒,所以还是使用LZO
的压缩方式,以空间换时间。但默认CONFIG_KERNEL_LZO
配置是找不到的,需要修改Kconfig。
# /arch/arm/Kconfig
- select HAVE_ARCH_KGDB
+ #select HAVE_ARCH_KGDB
# init/Kconfig
config HAVE_KERNEL_LZO
bool
- default n
+ default y
另外有些配置是明显可以减小内核大小的,并且在其他平台也适用。
CONFIG_LOCALVERSION
CONFIG_LOCALVERSION_AUTO
# 这项选中编译的时候会加-O2选项,能减小内核大小
CONFIG_CC_OPTIMIZE_FOR_SIZE
去掉不必要的文件系统支持和关闭内核调试都能明显减小内核大小。 另外一些驱动可以编译成模块,放在用户空间去加载,加载的时机可以是在Launcher启动后。 内核最后的启动时间大约是1.8S
[1.787044 0.000182] Starting kernel ...
[1.788860 0.001816]
[1.789454 0.000594] Uncompressing Linux... done, booting the kernel.
[3.565445 1.775991] root@sabresd_6dq:/ #
Frameworks
从启动流程可知,Kernel启动init->Zygote->SystemServer->ActivityManager->Launcher。 启动Zygote和SystemServer是优化的重点
Zygote优化
Zygote耗时主要在资源和类预加载部分,可以多线程优化,速度能有一定的提升,但前提是CPU支持多核。
private static Thread mClsThread = new Thread(new Runnable(){
public void run() {
preloadClasses();
}
});
private static Thread mResThread = new Thread(new Runnable(){
public void run() {
preloadResources();
}
});
private static Thread mGlThread = new Thread(new Runnable() {
public void run() {
preloadOpenGL();
}
});
static void preload() {
try {
mClsThread.start();
mResThread.start();
mGlThread.start();
mClsThread.join();
mResThread.join();
mGlThread.join();
} catch (InterruptedException e) {
Log.e(TAG,"MutiThread asyncPreload failed=====\n");
}
}
另一个就是设置Zygote进程优先级,减少preloadclass中的GC频率
//减少GC频率,modify begin
if (count%128==0&&Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {//end
if (false) {
Log.v(TAG,
" GC at " + Debug.getGlobalAllocSize());
}
System.gc();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
修改GC阈值
private static final int PRELOAD_GC_THRESHOLD = 64*1024*1024;
SystemServer优化
// to do
PackageManagerServicey优化
//doing
其他优化方案
- readahead
因为IO慢的原因(cpu与存储类设备如emmc通讯),有两段耗时的地方:1. Zygote的preload 资源和class;2. PackageManagerService的包扫描。可以采用Linux上使用较多的readahead机制,大概原理是:
统计开机过程中,读取的块数据信息,记录下来保存;
再次开机,通过记录下来的块数据读取信息,直接起一个服务,预先开始读,zygote或packagemanagerservice要读文件的时候,文件数据已经在cache中了。这样主要IO时间,跑到readahead进程去了。
但实际尝试中发现效果并不明显,而且因为readahead需要内核中加入trace,那样内核会变大很多,也会影响时间。
参考资料:
android bootchart 分析开机启动时间
Android开机速度优化简单回顾
添加readahead trace event
readahead进程实现
注意在自己添加内核trace event的时候,需要在内核中添加
# define CREATE_TRACE_POINTS
- BLCR&DMTCP 这两种方式主要是优化Zygote预加载的时间,大概原理是将正在运行于linux上的应用当前的运行点保存成为一个文件并且在以后的时间可以按照需要将该程序直接恢复到保存时候的状态。但这两种方式都比较复制,且容易造成系统不稳定,最后放弃了。 参考资料: 利用BLCR加快Android启动速度 android-checkpoing