Android Studio 如何分析 CPU 活動(dòng)
前面的小節(jié)我們學(xué)習(xí)了應(yīng)用開發(fā),構(gòu)建,發(fā)布,接下來幾個(gè)小結(jié)我們學(xué)習(xí)如何剖析應(yīng)用性能。本小節(jié)我們將學(xué)習(xí)如何分析 CPU 活動(dòng)。
1. 什么是 CPU Profiler
1.1 CPU Profiler 概覽
優(yōu)化應(yīng)用的 CPU 使用率能帶來諸多好處,如提供更快、更順暢的用戶體驗(yàn),以及延長(zhǎng)設(shè)備電池續(xù)航時(shí)間。我們可以使用 CPU Profiler 在與應(yīng)用交互時(shí)實(shí)時(shí)檢查應(yīng)用的 CPU 使用率和線程活動(dòng),也可以檢查記錄的方法跟蹤數(shù)據(jù)、函數(shù)跟蹤數(shù)據(jù)和系統(tǒng)跟蹤數(shù)據(jù)的詳細(xì)信息。
CPU Profiler 記錄和顯示的特定信息類型由我們選擇的記錄配置確定:
系統(tǒng)跟蹤數(shù)據(jù)
捕獲精細(xì)的詳細(xì)信息,以便我們檢查應(yīng)用與系統(tǒng)資源的交互情況。
方法和函數(shù)跟蹤數(shù)據(jù)
對(duì)于應(yīng)用進(jìn)程中的每個(gè)線程,我們可以了解一段時(shí)間內(nèi)執(zhí)行了哪些方法 (Java) 或函數(shù) (C/C++),以及每個(gè)方法或函數(shù)在其執(zhí)行期間消耗的 CPU 資源。我們還可以使用方法和函數(shù)跟蹤數(shù)據(jù)來識(shí)別調(diào)用方和被調(diào)用方。調(diào)用方是指調(diào)用其他方法或函數(shù)的方法或函數(shù),而被調(diào)用方是指被其他方法或函數(shù)調(diào)用的方法或函數(shù)。我們可以使用此信息來確定哪些方法或函數(shù)負(fù)責(zé)調(diào)用常常會(huì)消耗大量資源的特定任務(wù),并優(yōu)化應(yīng)用的代碼以避免不必要的工作。
1.2 打開 CPU Profiler
要打開 CPU Profiler,請(qǐng)按以下步驟操作:
依次選擇 View > Tool Windows > Profiler 或點(diǎn)擊工具欄中的 Profile 圖標(biāo) 。如果 Select Deployment Target 對(duì)話框顯示提示,請(qǐng)選擇要將我們的應(yīng)用部署到哪個(gè)設(shè)備上以進(jìn)行性能剖析。
點(diǎn)擊 CPU 時(shí)間軸上的任意位置以打開 CPU Profiler。當(dāng)打開 CPU Profiler 時(shí),它會(huì)立即開始顯示應(yīng)用的 CPU 使用率和線程活動(dòng)。系統(tǒng)會(huì)顯示類似于下圖的界面。
-
事件時(shí)間軸:顯示應(yīng)用中的 Activity 在其生命周期內(nèi)不斷轉(zhuǎn)換而經(jīng)歷各種不同狀態(tài)的過程,并指示用戶與設(shè)備的交互,包括屏幕旋轉(zhuǎn)事件;
-
CPU 時(shí)間軸:顯示應(yīng)用的實(shí)時(shí) CPU 使用率(以占總可用 CPU 時(shí)間的百分比表示)以及應(yīng)用當(dāng)前使用的線程總數(shù)。 此時(shí)間軸還會(huì)顯示其他進(jìn)程(如系統(tǒng)進(jìn)程或其他應(yīng)用)的 CPU 使用率,以便我們可以將其與我們應(yīng)用的 CPU 使用率進(jìn)行對(duì)比。我們可以通過沿時(shí)間軸的橫軸方向移動(dòng)鼠標(biāo)來檢查歷史 CPU 使用率數(shù)據(jù);
-
線程活動(dòng)時(shí)間軸:列出屬于應(yīng)用進(jìn)程的每個(gè)線程,并使用下面列出的顏色在時(shí)間軸上指示它們的活動(dòng)。記錄跟蹤數(shù)據(jù)后,我們可以從此時(shí)間軸上選擇一個(gè)線程,以在跟蹤數(shù)據(jù)窗格中檢查其數(shù)據(jù)。
-
綠色:表示線程處于活動(dòng)狀態(tài)或準(zhǔn)備使用 CPU。也就是說,線程處于正在運(yùn)行或可運(yùn)行狀態(tài)。
-
黃色:表示線程處于活動(dòng)狀態(tài),但它正在等待一項(xiàng) I/O 操作(如磁盤或網(wǎng)絡(luò) I/O),然后才能完成它的工作。
-
灰色:表示線程處于休眠狀態(tài)并且沒有占用任何 CPU 時(shí)間。 當(dāng)線程需要訪問尚不可用的資源時(shí),就會(huì)出現(xiàn)這種情況。在這種情況下,要么線程主動(dòng)進(jìn)入休眠狀態(tài),要么內(nèi)核將線程置于休眠狀態(tài),直到所需的資源可用。
-
2. 記錄 CPU 活動(dòng)
2.1 記錄跟蹤數(shù)據(jù)
要開始記錄跟蹤 CPU 活動(dòng)數(shù)據(jù),請(qǐng)從 CPU Profiler 頂部的下拉菜單中選擇記錄配置,然后點(diǎn)擊 Record,如下圖,CPU Profiler 顯示了正在進(jìn)行的記錄的狀態(tài)、持續(xù)時(shí)間和類型。
與我們的應(yīng)用交互,然后在完成時(shí)點(diǎn)擊 Stop。分析器將自動(dòng)選擇記錄的時(shí)間范圍,并在跟蹤數(shù)據(jù)窗格中顯示其跟蹤信息,如下圖所示。如果要檢查其他線程的跟蹤數(shù)據(jù),請(qǐng)從線程活動(dòng)時(shí)間軸上選擇相應(yīng)線程。
-
選定范圍:確定要在跟蹤數(shù)據(jù)窗格中檢查所記錄時(shí)間的哪一部分。當(dāng)我們首次記錄跟蹤數(shù)據(jù)時(shí),CPU Profiler 會(huì)自動(dòng)在 CPU 時(shí)間軸上選擇記錄的完整長(zhǎng)度。要僅檢查已記錄的時(shí)間范圍中的一部分的跟蹤數(shù)據(jù),請(qǐng)拖動(dòng)突出顯示區(qū)域的邊緣;
-
時(shí)間戳:指示所記錄跟蹤數(shù)據(jù)的開始和結(jié)束時(shí)間(相對(duì)于分析器開始收集 CPU 使用率信息的時(shí)間)。要選擇完整的記錄,請(qǐng)點(diǎn)擊時(shí)間戳;
-
跟蹤數(shù)據(jù)窗格:顯示我們選擇的時(shí)間范圍和線程的跟蹤數(shù)據(jù)。此窗格要在我們至少記錄一條跟蹤數(shù)據(jù)后才會(huì)顯示。 在此窗格中,我們可以選擇如何查看每個(gè)堆棧軌跡(使用跟蹤數(shù)據(jù)標(biāo)簽頁),以及如何測(cè)量執(zhí)行時(shí)間(使用時(shí)間參考下拉菜單);
-
跟蹤數(shù)據(jù)窗格標(biāo)簽頁:選擇如何顯示跟蹤數(shù)據(jù)詳細(xì)信息;
-
時(shí)間參考菜單:選擇以下選項(xiàng)之一,以確定如何測(cè)量每次調(diào)用的時(shí)間信息:
- Wall clock time:該時(shí)間信息表示實(shí)際經(jīng)過的時(shí)間。
- Thread time:該時(shí)間信息表示實(shí)際經(jīng)過的時(shí)間減去線程沒有占用 CPU 資源的那部分時(shí)間。對(duì)于任何給定的調(diào)用,其線程時(shí)間始終小于或等于其掛鐘時(shí)間。使用線程時(shí)間可以讓我們更好地了解線程的實(shí)際 CPU 使用率中有多少是給定方法或函數(shù)占用的。
2.2 選擇記錄配置
在開始記錄跟蹤信息之前,請(qǐng)為要捕獲的分析信息選擇適當(dāng)?shù)挠涗浥渲茫?/p>
-
對(duì) Java 方法采樣:在應(yīng)用的 Java 代碼執(zhí)行期間,頻繁捕獲應(yīng)用的調(diào)用堆棧。分析器會(huì)比較捕獲的數(shù)據(jù)集,以推導(dǎo)與應(yīng)用的 Java 代碼執(zhí)行有關(guān)的時(shí)間和資源使用信息;
-
跟蹤 Java 方法:在運(yùn)行時(shí)檢測(cè)應(yīng)用,以在每個(gè)方法調(diào)用開始和結(jié)束時(shí)記錄一個(gè)時(shí)間戳。系統(tǒng)會(huì)收集并比較這些時(shí)間戳,以生成方法跟蹤數(shù)據(jù),包括時(shí)間信息和 CPU 使用率;
-
對(duì) C/C++ 函數(shù)采樣:捕獲應(yīng)用的原生線程的采樣跟蹤數(shù)據(jù)。要使用此配置,我們必須將應(yīng)用部署到搭載 Android 8.0(API 級(jí)別 26)或更高版本的設(shè)備上;
-
跟蹤系統(tǒng)調(diào)用:捕獲非常翔實(shí)的細(xì)節(jié),以便我們檢查應(yīng)用與系統(tǒng)資源的交互情況。我們可以檢查線程狀態(tài)的確切時(shí)間和持續(xù)時(shí)間、直觀地查看所有內(nèi)核的 CPU 瓶頸在何處,并添加要分析的自定義跟蹤事件。 當(dāng)我們排查性能問題時(shí),此類信息至關(guān)重要。要使用此配置,我們必須將應(yīng)用部署到搭載 Android 7.0(API 級(jí)別 24)或更高版本的設(shè)備上。
2.3 Debug API 記錄 CPU 活動(dòng)
除了手動(dòng)點(diǎn)擊 Record 和 Stop 來記錄跟蹤數(shù)據(jù)外,我們還可以在代碼中使用 Debug API 讓應(yīng)用能夠在 CPU Profiler 中開始和停止記錄 CPU 活動(dòng)。
當(dāng)我們的應(yīng)用調(diào)用 startMethodTracing(String tracePath) 時(shí),CPU Profiler 將開始記錄;當(dāng)我們的應(yīng)用調(diào)用 stopMethodTracing() 時(shí),CPU Profiler 將停止記錄。在記錄使用此 API 觸發(fā)的 CPU 活動(dòng)時(shí),CPU Profiler 會(huì)將 Debug API 顯示為作用中的 CPU 記錄配置。
Tips:要使用 Debug API 控制 CPU 活動(dòng)的記錄,請(qǐng)將檢測(cè)的應(yīng)用部署到搭載 Android 8.0(API 級(jí)別 26)或更高版本的設(shè)備上。
2.4 記錄應(yīng)用啟動(dòng)的 CPU 活動(dòng)
要在應(yīng)用啟動(dòng)過程中自動(dòng)開始記錄 CPU 活動(dòng),請(qǐng)執(zhí)行以下操作:
- 依次選擇 Run > Edit Configurations;
2. 在 Profiling 標(biāo)簽中,勾選 Start recording a method trace on startup 旁邊的復(fù)選框;
-
從菜單中選擇 CPU 記錄配置;
-
點(diǎn)擊 Apply;
-
依次選擇 Run > Profile,將我們的應(yīng)用部署到搭載 Android 8.0(API 級(jí)別 26)或更高版本的設(shè)備上。
3. 導(dǎo)入導(dǎo)出 CPU 活動(dòng)
3.1 導(dǎo)出跟蹤數(shù)據(jù)
使用 CPU Profiler 記錄 CPU 活動(dòng)后,我們可以將相應(yīng)數(shù)據(jù)導(dǎo)出為 .trace 文件,以便與他人共享或日后進(jìn)行檢查。
要從 CPU 時(shí)間軸導(dǎo)出跟蹤文件,請(qǐng)執(zhí)行以下操作:
-
在 CPU 時(shí)間軸上,右鍵點(diǎn)擊要導(dǎo)出的記錄的方法跟蹤數(shù)據(jù)或系統(tǒng)跟蹤數(shù)據(jù);
-
從菜單中選擇 Export trace;
-
瀏覽到要保存文件的目標(biāo)位置,指定文件名,然后點(diǎn)擊 OK。
要從 Sessions 窗格導(dǎo)出跟蹤文件,請(qǐng)執(zhí)行以下操作:
-
在 Sessions 窗格中,右鍵點(diǎn)擊要導(dǎo)出的記錄的跟蹤數(shù)據(jù);
-
點(diǎn)擊會(huì)話條目右側(cè)的 Export method trace 或 Export system trace 按鈕;
-
瀏覽到要保存文件的目標(biāo)位置,指定文件名,然后點(diǎn)擊 OK。
3.2 導(dǎo)入跟蹤數(shù)據(jù)
我們可以導(dǎo)入使用 Debug API 或 CPU Profiler 創(chuàng)建的 .trace 文件。
要導(dǎo)入跟蹤文件,請(qǐng)?jiān)诜治銎鞯?Sessions 窗格中點(diǎn)擊 Start new profiler session 圖標(biāo) ,然后選擇 Load from file。
我們可以檢查導(dǎo)入到 CPU Profiler 中的跟蹤數(shù)據(jù),就像檢查直接在 CPU Profiler 中捕獲的跟蹤數(shù)據(jù)一樣,但有下面幾點(diǎn)不同:
-
CPU 活動(dòng)未顯示在 CPU 時(shí)間軸上。
-
線程活動(dòng)時(shí)間軸僅指明了可在哪里獲取各線程的跟蹤數(shù)據(jù),而未指明實(shí)際線程狀態(tài)(如運(yùn)行中、等待中或休眠中)。
4. 檢查 CPU 活動(dòng)
CPU Profiler 中的跟蹤數(shù)據(jù)窗格提供多個(gè)標(biāo)簽頁,供我們選擇如何查看所記錄的跟蹤數(shù)據(jù)中的信息。
要查看方法跟蹤數(shù)據(jù)和函數(shù)跟蹤數(shù)據(jù),我們可以從 Call Chart、Flame Chart、Top Down 和 Bottom Up 標(biāo)簽頁中進(jìn)行選擇。要查看系統(tǒng)跟蹤數(shù)據(jù),我們可以從 Trace Events、Flame Chart、Top Down 和 Bottom Up 標(biāo)簽頁中進(jìn)行選擇。
4.1 Call Chart 標(biāo)簽頁
Call Chart 標(biāo)簽頁會(huì)以圖形來呈現(xiàn)方法跟蹤數(shù)據(jù)或函數(shù)跟蹤數(shù)據(jù),其中調(diào)用的時(shí)間段和時(shí)間在橫軸上表示,而其被調(diào)用方則在縱軸上顯示。對(duì)系統(tǒng) API 的調(diào)用顯示為橙色,對(duì)應(yīng)用自有方法的調(diào)用顯示為綠色,對(duì)第三方 API(包括 Java 語言 API)的調(diào)用顯示為藍(lán)色。
上圖顯示了一個(gè)調(diào)用圖表示例,說明了給定方法或函數(shù)的 Self 時(shí)間、Children 時(shí)間和 Total 時(shí)間的概念。
4.2 Flame Chart 標(biāo)簽頁
Flame Chart 標(biāo)簽頁提供一個(gè)倒置的調(diào)用圖表,用來匯總完全相同的調(diào)用堆棧。也就是說,將具有相同調(diào)用方順序的完全相同的方法或函數(shù)收集起來,并在火焰圖中將它們表示為一個(gè)較長(zhǎng)的橫條(而不是將它們顯示為多個(gè)較短的橫條,如調(diào)用圖表中所示)。這樣更方便我們查看哪些方法或函數(shù)消耗的時(shí)間最多。不過,這也意味著,橫軸不代表時(shí)間軸,而是表示執(zhí)行每個(gè)方法或函數(shù)所需的相對(duì)時(shí)間。
為幫助說明此概念,不妨考慮下圖中的調(diào)用圖表。請(qǐng)注意,方法 D 多次調(diào)用 B(B1、B2 和 B3),其中一些對(duì) B 的調(diào)用也調(diào)用了 C(C1 和 C3)。
由于 B1、B2 和 B3 具有相同的調(diào)用方順序 (A → D → B),因此系統(tǒng)將它們匯總在一起,如下圖所示。同樣,也將 C1 和 C3 匯總在一起,因?yàn)樗鼈円簿哂邢嗤恼{(diào)用方順序 (A → D → B → C)。請(qǐng)注意,C2 不包括在內(nèi),因?yàn)樗哂胁煌恼{(diào)用方順序 (A → D → C)。
匯總的調(diào)用用于創(chuàng)建火焰圖,如下圖所示。 請(qǐng)注意,對(duì)于火焰圖中的任何給定調(diào)用,占用最多 CPU 時(shí)間的被調(diào)用方最先顯示。
4.3 Top Down 和 Bottom Up 標(biāo)簽頁
Top Down 標(biāo)簽顯示一個(gè)調(diào)用列表,在該列表中展開方法或函數(shù)節(jié)點(diǎn)會(huì)顯示它的被調(diào)用方。
如下圖所示,在 Top Down 標(biāo)簽頁中展開方法 A 的節(jié)點(diǎn)會(huì)顯示它的被調(diào)用方,即方法 B 和 D。在此之后,展開方法 D 的節(jié)點(diǎn)會(huì)顯示它的被調(diào)用方,即方法 B 和 C,依此類推。與 Flame chart 標(biāo)簽頁類似, Top Down 樹也匯總了具有相同調(diào)用堆棧的完全相同的方法的跟蹤信息。也就是說,F(xiàn)lame chart 標(biāo)簽頁提供了 Top down 標(biāo)簽頁的圖形表示方式。
Top Down 標(biāo)簽提供以下信息來幫助說明在每個(gè)調(diào)用上所花的 CPU 時(shí)間(時(shí)間也可表示為在選定范圍內(nèi)占線程總時(shí)間的百分比):
-
Self:方法或函數(shù)調(diào)用在執(zhí)行自己的代碼(而非被調(diào)用方的代碼)上所花的時(shí)間;
-
Children:方法或函數(shù)調(diào)用在執(zhí)行它的被調(diào)用方(而非自己的代碼)上所花的時(shí)間;
-
Total:方法的 Self 時(shí)間和 Children 時(shí)間的總和。這表示應(yīng)用在執(zhí)行調(diào)用時(shí)所用的總時(shí)間。
Bottom Up 標(biāo)簽頁顯示一個(gè)調(diào)用列表,在該列表中展開函數(shù)或方法的節(jié)點(diǎn)會(huì)顯示它的調(diào)用方。如下圖,提供了方法 C 的 Bottom Up 樹。在該 Bottom Up 樹中打開方法 C 的節(jié)點(diǎn)會(huì)顯示它獨(dú)有的各個(gè)調(diào)用方,即方法 B 和 D。請(qǐng)注意,盡管 B 調(diào)用 C 兩次,但在“Bottom Up”樹中展開方法 C 的節(jié)點(diǎn)時(shí),B 僅顯示一次。在此之后,展開 B 的節(jié)點(diǎn)會(huì)顯示它的調(diào)用方,即方法 A 和 D。
Bottom Up 標(biāo)簽頁用于按照占用的 CPU 時(shí)間由多到少(或由少到多)的順序?qū)Ψ椒ɑ蚝瘮?shù)排序。我們可以檢查每個(gè)節(jié)點(diǎn)以確定哪些調(diào)用方在調(diào)用這些方法或函數(shù)上所花的 CPU 時(shí)間最多。 與 Top Down 樹相比, Bottom Up 樹中每個(gè)方法或函數(shù)的時(shí)間信息參照的是每個(gè)樹頂部的方法(頂部節(jié)點(diǎn))。 CPU 時(shí)間也可表示為在該記錄期間占線程總時(shí)間的百分比。
4.4 Trace Events 標(biāo)簽頁
檢查系統(tǒng)跟蹤數(shù)據(jù)時(shí),我們可以使用 Trace Events 標(biāo)簽查看每個(gè)線程上發(fā)生的事件的詳細(xì)信息。
要查看某個(gè)線程的詳細(xì)信息,請(qǐng)?jiān)?Threads 窗格中選擇該線程。這樣將在 Kernel 窗格中突出顯示該線程在每個(gè) CPU 內(nèi)核上的活動(dòng),并在 Trace Events 標(biāo)簽頁中顯示該線程的事件。在 Trace Events 標(biāo)簽頁中將鼠標(biāo)指針懸停在某個(gè)事件上可查看該事件的名稱以及在每種狀態(tài)下所用的時(shí)間。
例如,下圖中,在 Threads 窗格中選擇了 RenderThread,在 Kernel 窗格中突出顯示了該線程在 CPU 0 和 CPU 1 上的活動(dòng),并在 Trace Events 標(biāo)簽頁中顯示了在特定事件上所花的時(shí)間。
5. 小結(jié)
本節(jié)課程我們主要學(xué)習(xí)了如何記錄和分析 CPU 活動(dòng)。本節(jié)課程的重點(diǎn)如下:
- 掌握如何記錄跟蹤 CPU 數(shù)據(jù);
- 掌握如何檢查分析 CPU 數(shù)據(jù)。