Android效能優化:CPU Profiler
為什麼要分析 CPU 使用率CPU Profiler 可幫助您實時檢查應用的 CPU 使用率和執行緒 Activity,並記錄函式跟蹤,以便您可以優化和除錯您的應用程式碼。
最大限度減少應用的 CPU 使用率具有許多優勢,如提供更快更順暢的使用者體驗,以及延長裝置電池續航時間。 它還可幫助應用在各種新舊裝置上保持良好效能。 與應用互動時,您可以使用 CPU Profiler 監控 CPU 使用率和執行緒 Activity。
對於應用程序中的每個執行緒,您可以檢視一段時間內執行了哪些函式,以及在其執行期間每個函式消耗的 CPU 資源。 您還可以使用函式跟蹤來識別呼叫方和被呼叫方。 呼叫方指呼叫其他函式的函式,而被呼叫方是指被其他函式呼叫的函式。 您可以使用此資訊確定哪些函式負責呼叫常常會消耗大量特定資源的任務,並嘗試優化應用程式碼以避免不必要的工作。
CPU Profiler 概覽
當您開啟 CPU Profiler 時,它將立即開始顯示應用的 CPU 使用率和執行緒 Activity。 您應該會看到類似圖 1 的一些內容:
圖 1
如圖 1 所示,CPU Profiler 的預設檢視包括以下內容:
1.Event 時間線:顯示應用中在其生命週期不同狀態間轉換的 Activity,並表明使用者與裝置的互動,包括螢幕旋轉 Event。
2.CPU 時間線:顯示應用的實時 CPU 使用率(以佔總可用 CPU 時間的百分比表示)以及應用使用的匯流排程數。 此時間線還顯示其他程序的 CPU 使用率(如系統程序或其他應用),以便您可以將其與您的應用使用率進行對比。 通過沿時間線的水平軸移動滑鼠,您還可以檢查歷史 CPU 使用率資料。
3.執行緒 Activity 時間線:列出屬於應用程序的每個執行緒並使用下面列出的顏色沿時間線標示它們的 Activity。 在您記錄一個函式跟蹤後,您可以從此時間線中選擇一個執行緒以在跟蹤窗格中檢查其資料。
- 綠色: 表示執行緒處於活動狀態或準備使用 CPU。 即,它正在“執行中”或處於“可執行”狀態。
- 黃色: 表示執行緒處於活動狀態,但它正在等待一個 I/O 操作(如磁碟或網路 I/O),然後才能完成它的工作。
- 灰色: 表示執行緒正在休眠且沒有消耗任何 CPU 時間。 當執行緒需要訪問尚不可用的資源時偶爾會發生這種情況。 執行緒進入自主休眠或核心將此執行緒置於休眠狀態,直到所需的資源可用。
4.記錄配置:允許您選擇以下選項之一以確定分析器記錄函式跟蹤的方式。
- Sampled: 一個預設配置,在應用執行期間頻繁捕獲應用的呼叫堆疊。 分析器比較捕獲的資料集以推導與應用程式碼執行有關的時間和資源使用資訊。 基於“Sampled”的跟蹤的固有問題是,如果應用在捕獲呼叫堆疊後進入一個函式並在下一次捕獲前退出該函式,則分析器不會記錄該函式呼叫。 如果您對此類生命週期很短的跟蹤函式感興趣,您應使用“Instrumented”跟蹤。
- Instrumented: 一個預設配置,在執行時設定應用以在每個函式呼叫的開始和結束時記錄時間戳。 它收集時間戳並進行比較,以生成函式跟蹤資料,包括時間資訊和 CPU 使用率。 請注意,與設定每個函式關聯的開銷會影響執行時效能,並可能會影響分析資料,對於生命週期相對較短的函式,這一點更為明顯。 此外,如果應用短時間內執行大量函式,則分析器可能會迅速超出它的檔案大小限制,且不能再記錄更多跟蹤資料。
- Edit configurations: 允許您更改上述“Sampled”和“Instrumented”記錄配置的某些預設值,並將它們另存為自定義配置。 如需瞭解更多資訊,請轉到[建立記錄配置]部分。
5.記錄按鈕:用於開始和停止記錄函式跟蹤。 如需瞭解更多資訊,請轉到[記錄和檢查函式跟蹤]部分 。
記錄和檢查函式跟蹤
要開始記錄函式跟蹤,從下拉選單中選擇 Sampled或 Instrumented記錄配置,或選擇您建立的[自定義記錄配置],然後點選 Record。
start recording
與應用互動並在完成後點選 Stop recording。
stop
分析器將自動選擇記錄的時間範圍,並在函式跟蹤窗格中顯示其跟蹤資訊,如圖 2 所示。如果您想檢查另一個執行緒的函式跟蹤,只需從執行緒 Activity 時間線中選中它。
圖 2.記錄函式跟蹤後的 CPU Profiler
1.選擇時間範圍: 用於確定您要在跟蹤窗格中檢查所記錄時間範圍的哪一部分。 當您首次記錄函式跟蹤時,CPU Profiler 將在 CPU 時間線中自動選擇您的記錄的完整長度。 如果您想僅檢查所記錄時間範圍一小部分的函式跟蹤資料,您可以點選並拖動突出顯示的區域邊緣以修改其長度。
2.時間戳: 用於表示所記錄函式跟蹤的開始和結束時間(相對於分析器從裝置開始收集 CPU 使用率資訊的時間)。 在選擇時間範圍時,您可以點選時間戳以自動選擇完整記錄,如果您有多個要進行切換的記錄,則此做法尤其有用。
3.跟蹤窗格: 用於顯示您所選的時間範圍和執行緒的函式跟蹤資料。 僅在您至少記錄一個函式跟蹤後此窗格才會顯示。 在此窗格中,您可以選擇想如何檢視每個堆疊追蹤(使用跟蹤標籤),以及如何測量執行時間(使用時間引用下拉選單)。
4.選擇後,可通過 Top Down 樹、Bottom Up 樹、呼叫圖表或火焰圖的形式顯示您的函式跟蹤。 您可以在下文中瞭解每個跟蹤窗格標籤的更多資訊。
5.從下拉選單中選擇以下選項之一,以確定如何測量每個函式呼叫的時間資訊:
- Wall clock time:壁鐘時間資訊表示實際經過的時間。
- Thread time:執行緒時間資訊表示實際經過的時間減去執行緒沒有消耗 CPU 資源的任意時間部分。 對於任何給定函式,其執行緒時間始終少於或等於其壁鐘時間。 使用執行緒時間可以讓您更好地瞭解執行緒的實際 CPU 使用率中有多少是給定函式消耗的。
Call Chart標籤提供函式跟蹤的圖形表示形式,其中,水平軸表示函式呼叫(或呼叫方)的時間段和時間,並沿垂直軸顯示其被呼叫者。 對系統 API 的函式呼叫顯示為橙色,對應用自有函式的呼叫顯示為綠色,對第三方 API(包括 Java 語言 API)的函式呼叫顯示為藍色。 下面的圖 3 展示了一個呼叫圖表示例,並描繪了給定函式的 self time、children time 以及總時間的概念。 您可以在如何使用 Top Down和 Bottom Up檢查跟蹤部分詳細瞭解這些概念。
圖 3. 一個呼叫圖表示例,描繪了函式 D 的 self、children 及總時間
使用 Flame Chart 標籤檢查跟蹤提示: 若要跳轉到某個函式的原始碼,請右鍵點選該函式並選擇 Jump to Source。 這適用於任一跟蹤窗格標籤。
Flame Chart 標籤提供一個倒置的呼叫圖表,其彙總相同的呼叫堆疊。 即,收集共享相同呼叫方順序的完全相同的函式,並在火焰圖中用一個較長的橫條表示它們(而不是將它們顯示為多個較短的橫條,如呼叫圖表中所示)。 這樣更方便您檢視哪些函式消耗最多時間。 不過,這也意味著水平軸不再代表時間線,相反,它表示每個函式相對的執行時間。
為幫助說明此概念,請考慮以下圖 4 中的呼叫圖表。 請注意,函式 D 多次呼叫 B(B1、B2 和 B3),其中一些對 B 的呼叫也呼叫了 C(C1 和 C3)。
圖 4. 包含多個共享通用呼叫方順序的函式呼叫的呼叫圖表
由於 B1、B2 和 B3 共享相同的呼叫方順序 (A → D → B),因此,可將它們彙總在一起,如下所示。 同樣,將 C1 和 C3 彙總在一起,因為它們也共享相同的呼叫方順序 (A → D → B → C)—請注意,未包含 C2,因為它具有不同的呼叫方順序 (A → D → C)。
圖 5. 彙總共享相同呼叫堆疊的相同函式
彙總的函式呼叫用於建立火焰圖,如圖 6 所示。請注意,對於火焰圖中任何給定的函式呼叫,消耗最多 CPU 時間的被呼叫方首先顯示。
圖 6. 圖 4 中顯示的呼叫圖表的火焰圖表示形式 使用 Top Down 和 Bottom Up 檢查跟蹤
Top Down標籤顯示一個函式呼叫列表,在該列表中展開函式節點會顯示函式的被呼叫方。 圖 7 顯示圖 3 中呼叫圖表的“Top Down”圖表。圖表中的每個箭頭都從呼叫方指向被呼叫方。
如圖 7 所示,在“Top Down”標籤中展開函式 A 的節點可顯示它的被呼叫方,即函式 B 和 D。 然後,展開函式 D 的節點可顯示它的被呼叫方,即函式 B 和 C 等等。 與 Flame chart 標籤相似,“Top Down”樹彙總共享相同呼叫堆疊的相同函式的跟蹤資訊。 也就是說,Flame chart 標籤可提供Top down 標籤的圖形化表示形式。
Top Down標籤提供以下資訊以幫助說明在每個函式呼叫上所花費的 CPU 時間(時間也可以用執行緒總時間佔所選時間範圍的持續時間的百分比表示):
- Self: 表示函式呼叫在執行自己的程式碼(而非被呼叫方的程式碼)上所花的時間,如圖 3 中的函式 D 所示。
- Children: 表示函式呼叫在執行自己的被呼叫方(而非自己的程式碼)上所花的時間,如圖 3 中的函式 D 所示。
- 總和: 函式的 Self 和 Children 時間的總和。 這表示應用在執行函式呼叫上所花的總時間,如圖 3 中函式 D 所示。
圖7/8
Bottom Up標籤顯示一個函式呼叫列表,在該列表中展開函式節點將顯示函式的呼叫方。 通過使用圖 7 中展示的跟蹤示例,圖 8 為函式 C 提供了一個“Bottom Up”樹。 在“Bottom Up”樹中開啟函式 C 的節點可顯示它獨有的呼叫方,即函式 B 和 D。 請注意,儘管 B 呼叫 C 兩次,但在“Bottom Up”樹中展開函式 C 的節點時,B 僅顯示一次。 然後,展開 B 的節點顯示其呼叫方,即函式 A 和 D。
Bottom Up標籤用於按照消耗最多(最少)CPU 時間排序函式。 您可以檢查每個節點以確定在呼叫函式上哪些呼叫方花了最多 CPU 時間。 與“Top Down”樹相比,“Bottom Up”樹中的每個函式的時間資訊引用每個樹頂部的函式(頂部模式)。 CPU 時間也可表示為在該記錄期間佔線程總時間的百分比。 下表有助於闡述如何解釋頂部節點的時間資訊及其呼叫方函式(子節點)。
Self | Children | 合計 | |
---|---|---|---|
“Bottom Up”樹頂部的函式(頂部模式) | 表示函式在執行自己的程式碼(而非其被呼叫方的程式碼)上所花的時間。 與“Top Down”樹相比,此時間資訊表示在記錄的持續時間內對此函式所有呼叫的總和。 | 表示函式執行它的被呼叫方(而非它自己的程式碼)上所花的總時間。 與“Top Down”樹相比,此時間資訊表示在記錄的持續期間內所有對此函式被呼叫方的呼叫總和。 | self time 和 children time 的總和。 |
呼叫方函式(子節點) | 表示在由呼叫方呼叫時被呼叫方的總 self time。 以圖 8 中的“Bottom Up”樹為例,被 B 呼叫時,函式 B 的 self time 將等於函式 C 每個執行的 self time 的總和。 | 表示在由呼叫方呼叫時被呼叫方的總 children time。 以圖 8 中的“Bottom Up”樹為例,被 B 呼叫時,函式 B 的 children time 將等於函式 C 每個執行的 children time 的總和。 | self time 和 children time 的總和。 |
簡單瞭解每個選項的功能後,我們針對上面的 demo 來實踐下,這裡我們選擇 bottom up 選項,從上面圖看出,每個函式所展示的資訊有:Total(函式總共花費的時間)、%(所佔的百分比)、Self(函式自身花費的時間)、%(所佔的百分比)、Children(函式所呼叫的子函式花費的時間),%(所佔百分比)。可以看到 main() run() 這些方法的時間主要都是花費在呼叫子函式上。那麼我們先找出本身最耗時的方法,使用 Self 進行排序:
作者Github:https://github.com/yifei8