Android的開機(jī)速度,基本上沒人說(shuō)快的,通常移植完系統(tǒng)后,馬上要看的事情就是優(yōu)化開機(jī)時(shí)間,以下是簡(jiǎn)單回憶以下以前做優(yōu)化的那些事。
一?開機(jī)時(shí)間都花在哪?
優(yōu)化開機(jī)時(shí)間,通常做的首先是那有有沒有BUG,明顯不合理的先解決,由于開發(fā)階段穩(wěn)定性問題,一些地方可能延時(shí)加的大,或者頻率設(shè)的低,先記下來(lái),后面定期還會(huì)再看。這些先不看的話,一般拿到機(jī)器,我們統(tǒng)計(jì)開機(jī)時(shí)間,主要看如下幾個(gè)時(shí)間段分布:
開機(jī)按鍵時(shí)間、亮屏?xí)r間(基本固定,除非弄錯(cuò)了,基本檢查一遍確定)
uboot啟動(dòng)時(shí)間
內(nèi)核啟動(dòng)后到bootanim退出時(shí)間

二 內(nèi)核優(yōu)化
可以通過添加打印module init的log,來(lái)check每個(gè)module初始化時(shí)的時(shí)間。從而找到花費(fèi)時(shí)間比較多的module:
--- a/init/main.c
+++ b/init/main.c
@@-785,7+785,7@@int__init_or_module
do_one_initcall(initcall_tfn)
if(initcall_blacklisted(fn))
return
-EPERM;
-if(initcall_debug)
+if(1)
? ? ? ? ? ? ? ? ret =
do_one_initcall_debug(fn);
三 優(yōu)化方案:
-3?優(yōu)化建議:
preloadClasses()與preloadResources()可以放到兩個(gè)線程里面跑。
修改zygote的nice值,及thread priority。
http://androidxref.com/6.0.1_r10/xref/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java#590
中增加如下的修改:
在?EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,592SystemClock.uptimeMillis());593preload();594EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,595SystemClock.uptimeMillis()); 前增加修改
/* 20151013 optimize android boot begin */
//get the default priority.
int defaultPriority = Process.getThreadPriority(Process.myPid()) ;
//increase the priority .
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO) ;
在gcAndFinalize(); 增加
Process.setThreadPriority(defaultPriority) ;
/*? 20151013 optimize android boot end */
-2 系統(tǒng)剪裁也有助于提高系統(tǒng)的開機(jī)速度
提升CPU頻率 - 將所有CPU切換至性能模式
adb shell stop perf-hal-1-0
adb shell "echo 1 > /sys/devices/system/cpu/cpu0/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu1/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu2/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu3/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu4/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu5/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu6/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu7/online"
adb shell "echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu2/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu3/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu4/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu5/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu6/cpufreq/scaling_governor"
adb shell "echo performance > /sys/devices/system/cpu/cpu7/cpufreq/scaling_governor"
adb shell "echo performance > /sys/class/devfreq/soc:qcom,cpubw/governor"
adb shell "echo performance > /sys/class/devfreq/soc:qcom,mincpubw/governor"
adb shell "echo performance > /sys/class/devfreq/soc:qcom,memlat-cpu0/governor"
adb shell "echo performance > /sys/class/devfreq/soc:qcom, memlat-cpu6/governor"
提升GPU頻率
adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel"
adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"
-1?電源優(yōu)化
on init
? ? # Disable UFS powersaving
? ? write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 0
? ? write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 0
? ? write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 0
? ? write /sys/module/lpm_levels/parameters/sleep_disabled Y
on property:sys.boot_completed=1
? ? # Enable UFS powersaving
? ? write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 1
? ? write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 1
? ? write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 1
? ? write /sys/module/lpm_levels/parameters/sleep_disabled N
on charger
? ? # Enable UFS powersaving
? ? write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 1
? ? write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 1
? ? write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 1
? ? write /sys/class/typec/port0/port_type sink
? ? write /sys/module/lpm_levels/parameters/sleep_disabled N
0?bootgraph 用來(lái)分析內(nèi)核功能, 在kernel cmdline 增加 initcall_debug ,然后dmesg > boot.log? bootgraph.pl boot.log >? boot.svg
1通過一個(gè)比gzip更快的方式去解壓內(nèi)核鏡像;
2 去掉系統(tǒng)中一些不必要的log打??;
3 去掉一些系統(tǒng)中不需要的驅(qū)動(dòng)模塊;
4 啟動(dòng)時(shí)即以最大頻率(cpu/DDR)且多核一起跑;
5 將一些耗時(shí)大,對(duì)啟動(dòng)順序沒有要求的驅(qū)動(dòng)通過異步方式進(jìn)行加載(如下所示)

這里我們主要關(guān)注的是第三個(gè),也是優(yōu)化的重點(diǎn)。這部分時(shí)間,具體都在干啥,瓶頸是哪,可以通過bootchart很清楚的看到。以下結(jié)合以前抓的圖,簡(jiǎn)要說(shuō)一下(圖是很久之前抓的,比較懶,沒有再跑一遍過程)

上圖中bootanim的退出時(shí)間沒有截出來(lái),實(shí)際圖是有的,大約是33s的時(shí)候結(jié)束。
這里分析時(shí),我們是分了幾個(gè)時(shí)間段:
1 內(nèi)核開始啟動(dòng),到init進(jìn)程開始執(zhí)行。這個(gè)可以通過log看到。
2 init進(jìn)程執(zhí)行,主要是處理init.rc中的命令,到core和mainl類服務(wù)開始啟動(dòng)的時(shí)間,上圖中可以看到,服務(wù)大體都在一個(gè)時(shí)間點(diǎn)起來(lái)的,約7.5S時(shí),這之前的一大段空窗期,也是要重點(diǎn)看的
3 zygote啟動(dòng)時(shí)間
4 systemserver中各個(gè)服務(wù)啟動(dòng)時(shí)間
5 應(yīng)用啟動(dòng)(systemui/launcher/keyguard..)
以上,具體分析看每段時(shí)間:
第一點(diǎn)另外處理,具體分析打印看是否有異常,這個(gè)值一般是很小的,不合理要和BSP同事一起查一下原因。
第二個(gè)主要是init.rc執(zhí)行各種命令,這個(gè)可以通過在execute_one_command函數(shù)中統(tǒng)計(jì)測(cè)量 ,比如大于100ms的命令打印出來(lái),再分析定位原因,這里命令執(zhí)行時(shí)間長(zhǎng)基本算BUG,要和BSP工程師一起解決。
第三點(diǎn)主要zygote啟動(dòng)問題,主要慢的原因,是加載資源和類庫(kù),這個(gè)要讀nand,一般卡的時(shí)間比較長(zhǎng),圖中可以看到,zygote進(jìn)程一溜的小粉紅,說(shuō)明IO較多。這個(gè)preload過程消耗的時(shí)間,在logcat的log中,也會(huì)打印的,一般來(lái)說(shuō),都是在近10S左右。
第四個(gè),zygote初始化完后,會(huì)fork system_server。 system_server進(jìn)程啟動(dòng),耗時(shí)也是較長(zhǎng)的。根據(jù)以前統(tǒng)計(jì)分析的結(jié)果,這里的服務(wù)啟動(dòng),基本上都是花在packageManagerService的PackageScan中,這又是一個(gè)讀文件,卡在文件讀取中,時(shí)間長(zhǎng)短,和預(yù)制app及安裝的app數(shù)量有關(guān)
第五個(gè)時(shí)間,是基本都準(zhǔn)備ready后,啟動(dòng)launcher等應(yīng)用了,啟動(dòng)完成后,systemServer請(qǐng)求SurfaceFlinger殺了bootanimation,就啟動(dòng)完成了。
以上時(shí)間中,主要要優(yōu)化的,還是第三步和第四步的IO慢問題,其他可優(yōu)化的不多。比如CPU,常開四核performance模式啟動(dòng),也并沒提升多少,一般我們就不管了這個(gè)了。
咋優(yōu)化?
確定優(yōu)化方向后主要看怎么優(yōu)化這兩段耗時(shí)的地方:
1. Zygote的preload 資源和class
2. PackageManagerService的包掃描
這里的第一個(gè),最早之前有人直接是去掉preload或刪減,雖然可以加快一點(diǎn)開機(jī)速度,但是撿了芝麻丟了西瓜,根本不能這樣干~
我們最早做的實(shí)現(xiàn)方式,
2.1 是將preload做并行處理,畢竟現(xiàn)在都是多核處理器了,而且是preload是加載后還要解析處理的,并行會(huì)有一定幅度提升。
對(duì)于包掃描,這個(gè)不好拆成并行任務(wù),不像preload那么簡(jiǎn)單干凈??紤]過將PackageManager的信息序列化后存起來(lái),下次開機(jī)就不掃了,不過看起來(lái)改動(dòng)有點(diǎn)大,不太好搞,也放棄了。
PackageManagerService掃描、檢查APK安裝包信息
2.2 PMS對(duì)/system/framework,/system/app,/data/app,/data/app-private目錄中的APK掃描耗費(fèi)了大量的時(shí)間,如果預(yù)置的三方應(yīng)用很多,這樣啟動(dòng)的時(shí)間就會(huì)越長(zhǎng)。
優(yōu)化建議:
2.3 /system/app下的應(yīng)用,如果是預(yù)置應(yīng)用,在Android.mk建議加上LOCAL_DEX_PREOPT := true控制,在/system/vendor下的預(yù)置應(yīng)用,如果此應(yīng)用編譯時(shí)間比較長(zhǎng)的,也使用上LOCAL_DEX_PREOPT := true
2.4 盡量減少data區(qū)內(nèi)置app的數(shù)量,這個(gè)會(huì)嚴(yán)重影響開機(jī)速度,特別是第一次的開機(jī)速度。放在system的app 盡量生成odex 這樣會(huì)加快開機(jī)速度。
最后我們的實(shí)現(xiàn)的方式,就是linux上用的較多的readahead機(jī)制。具體實(shí)現(xiàn)細(xì)節(jié)就不展開說(shuō)了,原理就是:
1. 統(tǒng)計(jì)開機(jī)過程中,讀取的塊數(shù)據(jù)信息,記錄下來(lái)保存
2.再次開機(jī),通過記錄下來(lái)的塊數(shù)據(jù)讀取信息,直接起一個(gè)服務(wù),預(yù)先開始讀,zygote或packagemanagerservice要讀文件的時(shí)候,文件數(shù)據(jù)已經(jīng)在cache中了。
實(shí)際用下來(lái),這一招特別好,優(yōu)化非常明顯。以下是實(shí)現(xiàn)了一個(gè)readahead后的bootchart圖:

可以看到:
1. zygote和system_server都提速了
2. zygote和system_server的IO時(shí)間,都降低非常大
3. 主要IO時(shí)間,跑到readahead進(jìn)程中去了。
不過,以上實(shí)現(xiàn),還是有可優(yōu)化的地方:
1. readahead進(jìn)程可以再提前,在system分區(qū)掛載后立刻啟動(dòng),這樣zygote中的IO應(yīng)該可以再減小
2. 對(duì)system_server的IO,此時(shí)readahead已經(jīng)結(jié)束了,按理不應(yīng)該有了,這里還是有IO,這一般是后裝apk導(dǎo)致,這個(gè)可以把readahead做的更健壯一些,不要只學(xué)習(xí)開始的一兩次。
其他NB的優(yōu)化
另外還有一個(gè)很NB的技術(shù),就是STD。這個(gè)我們也搞過,花費(fèi)了大量的人力物力。STD開機(jī)時(shí)間,不算上uboot時(shí)間的話,基本都是在10S內(nèi),5~8S之間。不過這么NB的技術(shù),目前基本上也是廢棄了,用起來(lái)問題也挺多的:
1. 開機(jī)時(shí)間少了,關(guān)機(jī)時(shí)間拉長(zhǎng)。
由于是STD(Suspend to Disk),關(guān)機(jī)時(shí)需要將內(nèi)存數(shù)據(jù)寫入nand,這塊也是挺麻煩的事情
2. 穩(wěn)定性
本身STD弄起來(lái)就比較復(fù)雜,BUG挺多的,另外使用STD,就相當(dāng)于永不關(guān)機(jī)了,這也太考驗(yàn)系統(tǒng)軟件的穩(wěn)定性了...
3. 沒毛用
?? 一開始還能忽悠客戶,不過后來(lái)也沒人怎么關(guān)心這個(gè)feature了,平白給自己找活干,大家都不樂意使能它了。