Systrace?響應速度實戰(zhàn)?1?:了解響應速度原理
[導讀]在討論Android性能問題的時候,卡頓、響應速度、ANR這三個性能相關的知識點通常會放到一起來講,因為引起卡頓、響應慢、ANR的原因類似,只不過根據重要程度,被人為分成了卡頓、響應慢、ANR三種,所以我們可以定義廣義上的卡頓,包含了卡頓、響應慢和ANR三種,所以如果用戶反饋說手...
在討論 Android 性能問題的時候,卡頓、響應速度、 ANR 這三個性能相關的知識點通常會放到一起來講,因為引起卡頓、響應慢、ANR 的原因類似,只不過根據重要程度,被人為分成了卡頓、響應慢、ANR 三種,所以我們可以定義廣義上的卡頓,包含了卡頓、響應慢和 ANR 三種,所以如果用戶反饋說手機卡頓或者 App 卡頓,大部分情況下都是廣義上的卡頓,需要搞清楚,到底出現(xiàn)了哪一種問題
0. 性能工程
在介紹響應速度的原理之前,這里先放一段 < 性能之巔 > 這本書中對于性能的描述,具體來說就是方法論,非常貼合本文的主題,也強烈推薦各位搞性能優(yōu)化的同學,把這本書作為手頭常讀的方法論書籍:
性能是充滿挑戰(zhàn)的
系統(tǒng)性能工程是一個充滿挑戰(zhàn)的領域,具體原因有很多,其中包括以下事實,系統(tǒng)性能是主觀的、復雜的,而且常常是多問題并存的
性能是主觀的
- 技術學科往往是客觀的,太多的業(yè)界人士審視問題非黑即白。在進行軟件故障查找的時候,判斷 bug 是否存在或 bug 是否修復就是這樣。bug 的出現(xiàn)總是伴隨著錯誤信息,錯誤信息通常容易解讀,進而你就明白錯誤為什么會出現(xiàn)了
- 與此不同,性能常常是主觀性的。開始著手性能問題的時候,對問題是否存在的判斷都有可能是模糊的,在問題被修復的時候也同樣,被一個用戶認為是 “不好” 的性能,另一個用戶可能認為是 “好” 的
系統(tǒng)是復雜的
- 除了主觀性之外,性能工程作為一門充滿了挑戰(zhàn)的學科,除了因為系統(tǒng)的復雜性,還因為對于性能,我們常常缺少一個明確的分析起點。有時我們只是從猜測開始,比如,責怪網絡,而性能分析必須對這是不是一個正確的方向做出判斷
- 性能問題可能出在子系統(tǒng)之間復雜的互聯(lián)上,即便這些子系統(tǒng)隔離時表現(xiàn)得都很好。也可能由于連鎖故障(cascading failure)出現(xiàn)性能問題,這指的是一個出現(xiàn)故障的組件會導致其他組件產生性能問題。要理解這些產生的問題,你必須理清組件之間的關系,還要了解它們是怎樣協(xié)作的
- 瓶頸往往是復雜的,還會以意想不到的方式互相聯(lián)系。修復了一個問題可能只是把瓶頸推向了系統(tǒng)里的其他地方,導致系統(tǒng)的整體性能并沒有得到期望的提升。
- 除了系統(tǒng)的復雜性之外,生產環(huán)境負載的復雜特性也可能會導致性能問題。在實驗室環(huán)境很難重現(xiàn)這類情況,或者只能間歇式地重現(xiàn)
- 解決復雜的性能問題常常需要全局性的方法。整個系統(tǒng) —— 包括自身內部和外部的交互 —— 都可能需要被調查研究。這項工作要求有非常廣泛的技能,一般不太可能集中在一人身上,這促使性能工程成為一門多變的并且充滿智力挑戰(zhàn)的工作
可能有多個問題并存
-- 以上幾段內容摘錄自 < 性能之巔 >
- 找到一個性能問題點往往并不是問題本身,在復雜的軟件中通常會有多個問題
- 性能分析的又一個難點:真正的任務不是尋找問題,而是辨別問題或者說是辨別哪些問題是最重要的
- 要做到這一點,性能分析必須量化(quantify)問題的重要程度。某些性能問題可能并不適用于你的工作負載或者只在非常小的程度上適用。理想情況下,你不僅要量化問題,還要估計每個問題修復后能帶來的增速。當管理層審查工程或運維資源的開銷緣由時,這類信息尤其有用。
- 有一個指標非常適合用來量化性能,那就是 延時(latency)
1. 響應速度概述
響應速度是應用 App 性能的重要指標之一。響應慢通常表現(xiàn)為點擊效果延遲、操作等待或白屏時間長等,主要場景包括:
- 應用啟動場景,包括冷啟動、熱啟動、溫啟動等
- 界面跳轉場景,包括應用內頁面跳轉、App 之間跳轉
- 其他非跳轉的點擊場景(開關、彈窗、長按、控件選擇、單擊、雙擊等)
- 亮滅屏、開關機、解鎖、人臉識別、拍照、視頻加載等場景
- 系統(tǒng)開發(fā)者 往往從 input 中斷開始看,部分以應用第一幀為結束點(因為比較好計算),部分以應用加載完成為結束點(比較主觀,除非結束點比較容易通過工具去判斷),主要是以優(yōu)化應用的整體性能為主,涉及到的方面就比較廣,包括 input 事件傳遞、SystemServer、SurfaceFlinger、Kernel 、Launcher 等
- App 開發(fā)者 一般從 Application 的 onCreate 或者 attachContext 開始看,大部分以界面完全加載或者用戶可操作為結束點,因為是自己的應用,結束點在代碼里面可以主動加,主要還是以優(yōu)化應用自身的啟動速度為主,市面上講啟動速度優(yōu)化的,大部分是講這部分
- 測試同學 則更多從用戶的真實體驗角度來看,以桌面點擊應用圖標且應用圖標變色為第一幀,內容完全加載為結束點。測試過程一般使用 高速相機 自動化,通過機械手和圖形識別技術,可以自動進行響應速度測試并抓取相關的測試數據
2. 響應速度問題分析思路
2.1 分清起點和終點
分析響應速度,最重要的是要找到起點和終點,上一節(jié)講到,不同角色的開發(fā)者,對這個性能指標的判定起點和終點都不一樣;而且這個指標有很主觀的成分,所以在開始的時候,就要跟各方來確定好起點和終點,具體的數值標準,下面一些手段可以幫助大家來確定
- 競品分析。一般來說,響應速度這個指標都會有一個對標的競品,競品手機或者競品 App,相同的條件下,競品手機或者競品 App 從點擊到響應花費了多少時間,可以作為一個標準
- 對比前一個版本。有時候系統(tǒng)進行大版本升級或者 App 進行版本迭代,那么上一個版本的數據就可以拿來作為標準進行對比
2.2 響應速度常見問題
2.2.1 Android 系統(tǒng)自身原因導致響應慢
下面這些列舉的是 Android 系統(tǒng)自身的原因,與 Android 機器的性能有比較大的關系,性能越差,越容易出現(xiàn)響應速度問題。下面就列出了 Android 系統(tǒng)原因導致的 App 響應速度出現(xiàn)問題的原因,以及這個時候 App 端在 Systrace 中的表現(xiàn)
CPU 頻率不足
App 端的表現(xiàn):主線程處于 Running 狀態(tài),但是執(zhí)行耗時變長
CPU 大小核調度:關鍵任務跑到了小核
App 端的表現(xiàn):Systrace 看主線程處于 Running 狀態(tài),但是執(zhí)行耗時變長
SystemServer 繁忙,主要影響
- 響應 App 主線程 Binder 調用處理耗時
- App 端的表現(xiàn):Systrace 看主線程處于 Sleep 狀態(tài),在等待 Binder 調用返回
- 應用啟動過程邏輯處理耗時
- App 端的表現(xiàn):Systrace 看主線程處于 Sleep 狀態(tài),在等待 Binder 調用返回
SurfaceFlinger 繁忙
主要影響應用的渲染線程的 dequeueBuffer、queueBuffer
- App 端的表現(xiàn):Systrace 看應用渲染線程的 dequeueBuffer、queueBuffer 處于 Binder 等待狀態(tài)
系統(tǒng)低內存 [3]
低內存的時候,很大概率出現(xiàn)下面幾種情況,都會對 SystemServer 和應用有影響
- 低內存的時候,有些應用會頻繁被殺和啟動,而應用啟動時一個重操作,會占用 CPU 資源,導致前臺 App 啟動變慢
- App 端的表現(xiàn):Systrace 看應用主線程 Runnable 狀態(tài)變多,Running 狀態(tài)變少,整體函數執(zhí)行耗時增加
- 低內存的時候,, 很容易觸發(fā)各個進程的 GC , , 用于內存回收的 HeapTaskDeamon、kswapd0 出現(xiàn)非常頻繁
- App 端的表現(xiàn):Systrace 看應用主線程 Runnable 狀態(tài)變多,Running 狀態(tài)變少,整體函數執(zhí)行耗時增加
- 低內存會導致磁盤 IO 變多,如果頻繁進行磁盤 IO , 由于磁盤 IO 很慢,那么主線程會有很多進程處于等 IO 的狀態(tài),也就是我們經常看到的 Uninterruptible Sleep
- App 端的表現(xiàn):Systrace 看應用主線程 Uninterruptible Sleep 和 Uninterruptible Sleep - IO 狀態(tài)變多,Running 狀態(tài)變少,整體函數執(zhí)行耗時增加
系統(tǒng)觸發(fā)溫控
頻率被限制:由于溫度過高,CPU 最高頻率被限制
整機 CPU 繁忙
可能有多個高負載進程同時在運行,或者有單個進程負載過高跑滿了 CPU
2.2.2 應用自身原因導致響應慢
應用自身原因主要是應用啟動時候的組件初始化、View 初始化、數據初始化耗時等,具體包括:
- Application.onCreate:應用自身的邏輯 三方 SDK 初始化耗時
- Activity 的生命周期函數:onStart、onCreate、onResume 耗時
- Services 的生命周期函數耗時
- Broadcast 的 onReceive 耗時
- ContentProvider 初始化耗時(注意已經被濫用)
- 界面布局初始化:measure、layout、draw 等耗時
- 渲染線程初始化:setSurface、queueBuffer、dequeueBuffer、Textureupload 等耗時
- Activity 跳轉:從 SplashActivity 到 MainActivity 耗時
- 應用向主線程 post 的耗時 Message 耗時
- 主線程或者渲染線程等待子線程數據更新耗時
- 主線程或者渲染線程等待子進程程數據更新耗時
- 主線程或者渲染線程等待網絡數據更新耗時
- 主線程或者渲染線程 binder 調用耗時
- WebView 初始化耗時
- 初次運行 JIT 耗時
2.3 響應速度問題分析套路(以 Systrace 為主)
2.3.1 確認前提條件
確認前提條件 (老化,數據量、下載等)、操作步驟、問題現(xiàn)象,本地復現(xiàn)步驟
2.3.2 需要明確測試標準
- 啟動時間的起點是哪里
- 啟動時機的終點是哪里
2.3.3 抓取所需的日志信息
主要包括 Systrace、常規(guī) log 、視頻、截圖等
2.3.4 分析 Systrace
首先進行 Systrace 分析,大概找出差異的點
2.3.4.1 分析耗時差異
首先查看應用耗時點,分析對比機差異,這里可以把應用啟動階段分成好幾段來看,來對比分析是哪部分時間增加
- Application 創(chuàng)建
- Activity 創(chuàng)建
- 第一個 doFrame
- 后續(xù)內容加載
- 應用自己的 Message
2.3.4.2 分析應用耗時的點
- 是否某一段方法自身執(zhí)行耗時比較久(Running 狀態(tài)) --> 應用自身問題
- 主線程是否有大段 Running 狀態(tài),但是底下沒有任何堆棧 --> 應用自身問題,加 TraceTag 或者使用 TraceView 來找到對應的代碼邏輯
- 是否在等 Binder 耗時比較久(Sleep 狀態(tài)) --> 檢測 Binder 服務端,一般是 SystemServer
- 是否在等待子線程返回數據(Sleep 狀態(tài)) --> 應用自身問題,通過查看 wakeup 信息,來找到依賴的子線程
- 是否在等待子進程返回數據(Sleep 狀態(tài)) --> 應用自身問題,通過查看 wakeup 信息,來找到依賴的子進程或者其他進程 (一般是 ContentProvider 所在的進程)
- 是否有大量的 Runnable --> 系統(tǒng)問題,查看 CPU 部分,看看是否已經跑滿
- 是否有大量的 IO 等待(Uninterruptible Sleep | WakeKill - Block I/O) --> 檢查系統(tǒng)是否已經低內存 [4]
- RenderThread 是否執(zhí)行 dequeueBuffer 和 queueBuffer 耗時 --> 查看 SurfaceFlinger
2.3.4.3 排查系統(tǒng)問題
如果分析是系統(tǒng)的問題,則根據上面耗時的點,查看系統(tǒng)對應的部分,一般情況要優(yōu)先查看系統(tǒng)是否異常,參考上面列出的的系統(tǒng)原因,主要看下面四個區(qū)域(Systrace)
- Kernel 區(qū)域 [5]
- 查看關鍵任務是否跑在了小核 --> 一般小核是 0-3(也有特例),如果啟動時候的關鍵任務跑到了小核,執(zhí)行速度也會變慢
- 查看頻率是否沒有跑滿 --> 表現(xiàn)是核心頻率沒有達到最大值,比如最大值是 2.8Ghz,但是只跑到了 1.8Ghz,那么可能是有問題的
- 查看 CPU 使用率,是否已經跑滿了 --> 表現(xiàn)是 CPU 區(qū)域八個核心上,任務和任務之間沒有空隙
- 查看是否處于低內存狀態(tài) [6],比如是否應用進程狀態(tài)有大量的 Uninterruptible Sleep | WakeKill - Block I/O、HeapTaskDeamon 任務執(zhí)行頻繁、kswapd0 任務執(zhí)行頻繁等
- SystemServer 進程區(qū)域 [7]
- input 事件讀取和分發(fā)是否有異常 --> 表現(xiàn)是 input 事件傳遞耗時,比較少見
- binder 執(zhí)行是否耗時 --> 表現(xiàn)是 SystemServer 對應的 Binder 執(zhí)行代碼邏輯耗時
- binder 等 am、wm 鎖是否耗時 --> 表現(xiàn)是 SystemServer 對應的 Binder 都在等待鎖,可以通過 wakeup 信息跟蹤等鎖情況,分析等鎖是不是由于應用導致的
- 是否有應用頻繁啟動或者被殺 --> 在 Systrace 中查看 startProcess,或者查看 Event Log
- SurfaceFlinger 進程區(qū)域 [8]
- dequeueBuffer 和 queueBuffer 是否執(zhí)行耗時 --> 表現(xiàn)是 SurfaceFlinger 的對應的 Binder 執(zhí)行 dequeueBuffer 和 queueBuffer 耗時
- 主線程是否執(zhí)行耗時 --> 表現(xiàn)是 SurfaceFlinger 主線程耗時,可能是在執(zhí)行其他的任務
- Launcher 進程區(qū)域(冷熱啟動場景)
- Launcher 進程處理點擊事件是否耗時 --> 表現(xiàn)在處理 input 事件耗時
- Launcher 自身 pause 是否耗時 --> 表現(xiàn)在執(zhí)行 onPause 耗時
- Launcher 應用啟動動畫是否耗時或者卡頓 --> 表現(xiàn)在動畫耗時或者卡頓
2.3.4.4 初步分析有懷疑的點之后
- 如果是系統(tǒng)的原因,首先需要看應用自身是否能規(guī)避,如果不能規(guī)避,則轉給系統(tǒng)來處理
- 如果是應用自身的原因,可以使用 TraceView(AS 自帶的 CPU Profiler)、Simple Perf 等繼續(xù)查看更加詳細的函數調用信息,也可以使用 TraceFix 插件 [9],插入更多的 TraceTag 之后,重新抓取 Systrace 來對比分析
2.3.4.5 問題可能有很多個原因
- 首先要把影響最大的因素找出來優(yōu)化,影響比較小的因素可以先忽略
- 有些問題需要系統(tǒng)的配合才能解決,這時候需要跟系統(tǒng)一起進行調優(yōu)(比如各大 App 廠商就會有專門跟手機廠商打交道的,手機廠商會以 SDK 的形式,暴露部分系統(tǒng)接口給 App 來使用,比如 Oppo 、華為、Vivo 等)
- 有些問題影響很小或者無解,這時候需要跟測試同學溝通清楚
- 有些問題是重復問題或不同平臺的相同,可以在 Bug 庫中搜索是否有案例
End
本篇文章主要是一個響應速度基礎知識方面的一個普及,其中涉及到大量的系統(tǒng)知識,不熟悉的同學可以跟著 Systrace 基礎知識系列 [10] 過一下





