Android的開機(jī)速度勤讽,基本上沒人說快的蟋座,通常移植完系統(tǒng)后,馬上要看的事情就是優(yōu)化開機(jī)時(shí)間脚牍,以下是簡單回憶以下以前做優(yōu)化的那些事向臀。
一?開機(jī)時(shí)間都花在哪?
優(yōu)化開機(jī)時(shí)間诸狭,通常做的首先是那有有沒有BUG券膀,明顯不合理的先解決,由于開發(fā)階段穩(wěn)定性問題驯遇,一些地方可能延時(shí)加的大芹彬,或者頻率設(shè)的低,先記下來叉庐,后面定期還會再看舒帮。這些先不看的話,一般拿到機(jī)器陡叠,我們統(tǒng)計(jì)開機(jī)時(shí)間玩郊,主要看如下幾個(gè)時(shí)間段分布:
開機(jī)按鍵時(shí)間、亮屏?xí)r間(基本固定枉阵,除非弄錯(cuò)了译红,基本檢查一遍確定)
uboot啟動時(shí)間
內(nèi)核啟動后到bootanim退出時(shí)間
二 內(nèi)核優(yōu)化
可以通過添加打印module init的log,來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 用來分析內(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ū)動模塊;
4 啟動時(shí)即以最大頻率(cpu/DDR)且多核一起跑淳蔼;
5 將一些耗時(shí)大侧蘸,對啟動順序沒有要求的驅(qū)動通過異步方式進(jìn)行加載(如下所示)
這里我們主要關(guān)注的是第三個(gè)裁眯,也是優(yōu)化的重點(diǎn)。這部分時(shí)間讳癌,具體都在干啥穿稳,瓶頸是哪,可以通過bootchart很清楚的看到晌坤。以下結(jié)合以前抓的圖逢艘,簡要說一下(圖是很久之前抓的,比較懶骤菠,沒有再跑一遍過程)
上圖中bootanim的退出時(shí)間沒有截出來它改,實(shí)際圖是有的,大約是33s的時(shí)候結(jié)束商乎。
這里分析時(shí)央拖,我們是分了幾個(gè)時(shí)間段:
1 內(nèi)核開始啟動,到init進(jìn)程開始執(zhí)行鹉戚。這個(gè)可以通過log看到鲜戒。
2 init進(jìn)程執(zhí)行,主要是處理init.rc中的命令抹凳,到core和mainl類服務(wù)開始啟動的時(shí)間袍啡,上圖中可以看到,服務(wù)大體都在一個(gè)時(shí)間點(diǎn)起來的却桶,約7.5S時(shí),這之前的一大段空窗期蔗牡,也是要重點(diǎn)看的
3 zygote啟動時(shí)間
4 systemserver中各個(gè)服務(wù)啟動時(shí)間
5 應(yīng)用啟動(systemui/launcher/keyguard..)
以上颖系,具體分析看每段時(shí)間:
第一點(diǎn)另外處理,具體分析打印看是否有異常辩越,這個(gè)值一般是很小的嘁扼,不合理要和BSP同事一起查一下原因。
第二個(gè)主要是init.rc執(zhí)行各種命令黔攒,這個(gè)可以通過在execute_one_command函數(shù)中統(tǒng)計(jì)測量 趁啸,比如大于100ms的命令打印出來,再分析定位原因督惰,這里命令執(zhí)行時(shí)間長基本算BUG不傅,要和BSP工程師一起解決。
第三點(diǎn)主要zygote啟動問題赏胚,主要慢的原因访娶,是加載資源和類庫,這個(gè)要讀nand觉阅,一般卡的時(shí)間比較長崖疤,圖中可以看到秘车,zygote進(jìn)程一溜的小粉紅,說明IO較多劫哼。這個(gè)preload過程消耗的時(shí)間叮趴,在logcat的log中,也會打印的权烧,一般來說眯亦,都是在近10S左右。
第四個(gè)豪嚎,zygote初始化完后搔驼,會fork system_server。 system_server進(jìn)程啟動侈询,耗時(shí)也是較長的舌涨。根據(jù)以前統(tǒng)計(jì)分析的結(jié)果,這里的服務(wù)啟動扔字,基本上都是花在packageManagerService的PackageScan中囊嘉,這又是一個(gè)讀文件,卡在文件讀取中革为,時(shí)間長短扭粱,和預(yù)制app及安裝的app數(shù)量有關(guān)
第五個(gè)時(shí)間,是基本都準(zhǔn)備ready后震檩,啟動launcher等應(yīng)用了琢蛤,啟動完成后,systemServer請求SurfaceFlinger殺了bootanimation抛虏,就啟動完成了博其。
以上時(shí)間中,主要要優(yōu)化的迂猴,還是第三步和第四步的IO慢問題慕淡,其他可優(yōu)化的不多。比如CPU沸毁,常開四核performance模式啟動峰髓,也并沒提升多少,一般我們就不管了這個(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是加載后還要解析處理的,并行會有一定幅度提升死遭。
對于包掃描广恢,這個(gè)不好拆成并行任務(wù),不像preload那么簡單干凈呀潭《っ裕考慮過將PackageManager的信息序列化后存起來,下次開機(jī)就不掃了钠署,不過看起來改動有點(diǎn)大糠聪,不太好搞,也放棄了谐鼎。
PackageManagerService掃描舰蟆、檢查APK安裝包信息
2.2 PMS對/system/framework,/system/app狸棍,/data/app身害,/data/app-private目錄中的APK掃描耗費(fèi)了大量的時(shí)間,如果預(yù)置的三方應(yīng)用很多草戈,這樣啟動的時(shí)間就會越長塌鸯。
優(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í)間比較長的费韭,也使用上LOCAL_DEX_PREOPT := true
2.4 盡量減少data區(qū)內(nèi)置app的數(shù)量淮悼,這個(gè)會嚴(yán)重影響開機(jī)速度,特別是第一次的開機(jī)速度揽思。放在system的app 盡量生成odex 這樣會加快開機(jī)速度。
最后我們的實(shí)現(xiàn)的方式见擦,就是linux上用的較多的readahead機(jī)制钉汗。具體實(shí)現(xiàn)細(xì)節(jié)就不展開說了,原理就是:
1. 統(tǒng)計(jì)開機(jī)過程中鲤屡,讀取的塊數(shù)據(jù)信息损痰,記錄下來保存
2.再次開機(jī),通過記錄下來的塊數(shù)據(jù)讀取信息酒来,直接起一個(gè)服務(wù)卢未,預(yù)先開始讀,zygote或packagemanagerservice要讀文件的時(shí)候,文件數(shù)據(jù)已經(jīng)在cache中了辽社。
實(shí)際用下來伟墙,這一招特別好,優(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ū)掛載后立刻啟動戏自,這樣zygote中的IO應(yīng)該可以再減小
2. 對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ù),目前基本上也是廢棄了淹魄,用起來問題也挺多的:
1. 開機(jī)時(shí)間少了郁惜,關(guān)機(jī)時(shí)間拉長。
由于是STD(Suspend to Disk)甲锡,關(guān)機(jī)時(shí)需要將內(nèi)存數(shù)據(jù)寫入nand兆蕉,這塊也是挺麻煩的事情
2. 穩(wěn)定性
本身STD弄起來就比較復(fù)雜,BUG挺多的缤沦,另外使用STD虎韵,就相當(dāng)于永不關(guān)機(jī)了,這也太考驗(yàn)系統(tǒng)軟件的穩(wěn)定性了...
3. 沒毛用
?? 一開始還能忽悠客戶缸废,不過后來也沒人怎么關(guān)心這個(gè)feature了包蓝,平白給自己找活干驶社,大家都不樂意使能它了。