Android開機(jī)速度優(yōu)化初探

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了,平白給自己找活干,大家都不樂意使能它了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容