Android 平臺側性能優化之應用啓動[問題已解決]

上一篇文章Android 平臺側性能優化之應用啓動採用多個命令分析了平臺的cpu/memory等方面對Email啓動慢照成的影響。很遺憾花了許多精力,但依然沒有找出問題所在,這個坑終於填平了。手動撒花^_^
回頭看來,問題其實很簡單,一開始走錯方向,導致花了許多精力,不過這個過程也同樣積累了不少知識。
本篇文章記錄填坑的過程,重新換個角度分析。

曙光出現:

同事在分析另外一個I/O讀取慢的問題時發現我們的設備是被加密的,得知這個消息後,我的內心是激動的。一度以爲找到了問題的root cause.

adb shell getprop ro.crypto.state

用上述命令查看問題機的加密狀態,返回encrypted,果然是已經默認加密了。感覺曙光出現了,我們的設備加密時將/data分區也進行了加密,而應用啓動的文件正是放在/data分區下的。按理來講加密後的/data分區讀寫速度肯定弱於未加密狀態。問題會不會跟此相關呢?
我們採用的是高通8909平臺芯片,找到對應的fstab.qcom文件。修改如下:


- /dev/block/bootdevice/by-name/userdata       /data        ext4    nosuid,nodev,barrier=1,noauto_da_alloc,discard      wait,check,forceencrypt=footer
+ /dev/block/bootdevice/by-name/userdata       /data        ext4    nosuid,nodev,barrier=1,noauto_da_alloc,discard      wait,check,encryptable=footer

關鍵就是修改這一句forceencrypt=footer===>encryptable=footer
forceencrypt表示強制加密,我們改成encryptable意味着加密是讓用戶主動去選擇的。

懷着胸有成竹的心情驗證修改後的版本。發現根本沒有起任何作用。whats’s the fuck!說不過去啊。
滿以爲找到突破口了,結果希望的火焰還是被無情的澆滅了。

還是IO速度

自己寫了一個test apk,專門測試平臺emcc的讀寫速度。測試問題機Log如下:

03-24 20:05:52.126 30578 30727 D Sunny-Test: start call writeData--->
03-24 20:05:52.144 30578 30727 D Sunny-Test-Utils: befWriteTime=93676689579157
03-24 20:05:52.144 30578 30727 D Sunny-Test-Utils: NewBufTime use time--->392292
03-24 20:06:12.656 30578 30727 D Sunny-Test-Utils: initBufData use time--->20512314888
03-24 20:06:12.656 30578 30727 D Sunny-Test-Utils: total create BufData use time--->20512707180
03-24 20:06:12.695 30578 30727 D Sunny-Test-Utils: aftWriteTime=93697240995816
03-24 20:06:12.695 30578 30727 D Sunny-Test-Utils: delta=20551416659
03-24 20:06:12.696 30578 30727 D Sunny-Test: start call writeData--->
03-24 20:06:12.710 30578 30727 D Sunny-Test-Utils: befWriteTime=93697255657795
03-24 20:06:12.710 30578 30727 D Sunny-Test-Utils: NewBufTime use time--->551198
03-24 20:06:33.188 30578 30727 D Sunny-Test-Utils: initBufData use time--->20477974992
03-24 20:06:33.188 30578 30727 D Sunny-Test-Utils: total create BufData use time--->20478526190
03-24 20:06:33.227 30578 30727 D Sunny-Test-Utils: aftWriteTime=93717772564923
03-24 20:06:33.227 30578 30727 D Sunny-Test-Utils: delta=20516907128

在測試參考機Log如下:

03-24 06:03:42.766 18541 18576 D Sunny-Test: start call writeData--->
03-24 06:03:42.780 18541 18576 D Sunny-Test-Utils: befWriteTime=7136372545141
03-24 06:03:42.787 18541 18576 D Sunny-Test-Utils: NewBufTime use time--->6635938
03-24 06:03:45.272 18541 18576 D Sunny-Test-Utils: initBufData use time--->2485714686
03-24 06:03:45.272 18541 18576 D Sunny-Test-Utils: total create BufData use time--->2492350624
03-24 06:03:45.359 18541 18576 D Sunny-Test-Utils: aftWriteTime=7138951725036
03-24 06:03:45.359 18541 18576 D Sunny-Test-Utils: delta=2579179895
03-24 06:03:45.360 18541 18576 D Sunny-Test: start call writeData--->
03-24 06:03:45.371 18541 18576 D Sunny-Test-Utils: befWriteTime=7138963657484
03-24 06:03:45.371 18541 18576 D Sunny-Test-Utils: NewBufTime use time--->387188
03-24 06:03:47.898 18541 18576 D Sunny-Test-Utils: initBufData use time--->2526351301
03-24 06:03:47.898 18541 18576 D Sunny-Test-Utils: total create BufData use time--->2526738489
03-24 06:03:47.983 18541 18576 D Sunny-Test-Utils: aftWriteTime=7141575492639
03-24 06:03:47.983 18541 18576 D Sunny-Test-Utils: delta=2611835155

可以看到
問題機寫入耗時:Sunny-Test-Utils: delta=20516907128
參考機寫入耗時:Sunny-Test-Utils: delta=2611835155
參考機比問題機寫入速度快了一個數量級。再用其他的工具測試I/O讀寫速度,同樣發現參考機比問題機快。但知道這個還是沒有辦法解決問題,也沒法量化I/O差異到底會對Email的啓動速度影響多大。
問題到這一度陷入窘境,迷失了分析方向。是時候祭出systrace工具了。

插播 systrace 使用簡介

systrace位於/platform-tools/systrace目錄下。
我們主要分析Email的性能問題,因此可以用命令:

python systrace.py –app=com.tct.email gfx view sched dalvik -o email_launch.html

來抓取trace log分析啓動過程。systrace 支持的trace類型可以通過:

python systrace.py -l

查看,用Android N下的systrace工具輸出結果如下:

systrace.py -l 輸出結果

輸入上述命令,然後啓動Email,待主界面展示出來後,根據提示輸入回車鍵,此時systrace工具開始生成報告。

生成systrace過程

Tips:
systrace.py位於SDK/platform-tools/systrace目錄下。抓取時最好用系統對應的SDK工具抓取。同時如果有源碼,也可以用源碼的external/chromium-trace/catapult/systrace/systrace/systrace.py 抓取。同樣注意抓取systrace的手機跟源碼版本一直。

然後打開chrome,在地址欄裏輸入

chrome://tracing/

load systrace界面

點擊Load 按鈕,載入email_launch.html文件。顯示入下圖:

systrace 主界面

查看systrace主要用到4個快捷鍵:

W 放大視圖
S 縮小視圖
A 右移視圖
D 左移視圖

# systrace 報告分析
爲了排除apk版本差異的干擾,將同版本的apk分別安裝到問題機跟參考機上。然後對比systrace報告。
先用TestActivityLaunchTime.py測試問題機以及參考機的啓動時間。腳本獲取地址:https://github.com/guanglixiang/Android_Performance/blob/master/TestActivityLaunchTime.py
問題機:

 **************************************************
 Test APK version info is:
 versionCode=317022801 minSdk=17 targetSdk=24
     versionName=v7.0.4.1.0312.0_0228
     **************************************************
     1 lunch time is 875
     2 lunch time is 884
     3 lunch time is 897
     4 lunch time is 873
     5 lunch time is 862
     5 launch time average is 878

參考機:


**************************************************
Test APK version info is:
versionCode=317022801 targetSdk=24
versionName=v7.0.4.1.0312.0_0228
versionCode=216030401 targetSdk=23
versionName=v5.2.10.3.0214.0
**************************************************
1 lunch time is 806
2 lunch time is 773
3 lunch time is 782
4 lunch time is 777
5 lunch time is 835
5 launch time average is 794

這裏看差距貌似不大,但多次測試總是發現參考機就是比問題機快那麼一點點。從配置上來將問題機跟參考機配置差不多,並且問題機的內存還比參考機大。爲什麼就是慢那麼一點點呢。
下面嘗試通過systrace文件去找原因。
這裏我用命令:

./systrace.py gfx input view wm am sm app res dalvik bionic pm database sched freq idle disk mmc load -t 5 -o vJ5E-no-encrypt-no-net_emailv7.html

分別抓取參考機和問題機的systrace。終於發現了問題所在。

參考機systrace

問題機systrace

從systrace對比非常明顯的看到參考機沒有bindApplication的過程,而問題機卻在這個過程中消耗了0.226s。
我們知道冷啓動應用都是需要走bindApplication過程的。
bindApplication過程會:
1.創建應用進程
2.加載應用dex執行文件
3.獲取應用資源文件
4.makeApplication,初始化JavaContextClassLoader。
相應的熱啓動是不需要走上述過程的。

爲了驗證上述分析。可以有個簡單的方法,只要先啓動Email,然後點擊menu清除最近的應用,最後通過ps命令查看email進程是否還存在。

清理應用結果

透過上面的分析可以確定參考機的Email應用一直都在後臺運行。而測試測冷啓動case時只是點擊menu鍵,然後清除所有應用,在去啓動Email。參考機的Email逃過了這一劫,清除的過程中,進程並沒有被殺死。而問題機的Email就沒那麼幸運了,直接被殺的它媽都不認識它了。

解決辦法

由於參考機將Email配置成了後臺一直運行,相當於拿問題機的冷啓動耗時對比參考機的熱啓動應用耗時。想要達到參考機的啓動速度,在平臺側可以也可以效仿參考機將Email配置成後臺一直運行。
修改方法:
在frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java

     ```
     void kill(String reason, boolean noisy) {
         if("com.tct.email".equals(processName) 
                && !KILL_APP_REASON_PERMISSIONS_REVOKED.equals(reason)) {
                            return;
                        }
                            //忽略其他code
                        }
    ```

但這種做法需要項目上根據需求做權衡。Email應用本身會定時去check郵件列表,發起網絡請求,屬於高耗電程序。一直在後臺運行會消耗電量。

寫在最後

Email問題陸陸續續的分析了兩週多,最開始一心要去從I/O速度上去分析個結果出來。沒有去想其它的可能。中間一度想放棄,打算將問題歸結到I/O,拋給底層同事去check。但心裏始終不安,有空了就去想該問題。其實一開始也抓取過systrace,只是重點放在了分析I/O上,沒有跟參考機做對比。沒有對比就帶來了傷害啊TT

systrace是個好東西,有空打算專門寫篇針對該工具的文章。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章