http://blog.csdn.net/innost/article/details/9008691
經作者授權,發表Tieto某青年牛的一篇《程序員》大作。
Android系統性能調優工具介紹
在軟件開發過程中,想必很多讀者都遇到過系統性能問題。而解決系統性能問題的幾個主要步驟是:
- 測評:對系統進行大量有針對性的測試,以得到合適的測試數據。
- 分析系統瓶頸:分析測試數據,找到其中的hotspot(熱點,即bottleneck)。
- 性能優化:對hotspot相關的代碼進行優化。
由上述步驟可知,性能優化的目標對象是hotspot。如果找到的hotspot並非真正的熱點,則性能優化的結果必然是事倍功半甚至竹籃打水一場空。所以,作為Android性能調優相關知識的第一部分,本篇首先將向讀者介紹Android平台中三個重要的性能測試工具,它們能很好得幫助開發者找到hotspot。
一Traceview介紹
1.1 Traceview簡介
Traceview是Android平台特有的數據采集和分析工具,它主要用於分析Android中應用程序的hotspot。Traceview本身只是一個數據分析工具,而數據的采集則需要使用Android SDK中的Debug類或者利用DDMS工具。二者的用法如下:
- 開發者在一些關鍵代碼段開始前調用Android SDK中Debug類的startMethodTracing函數,並在關鍵代碼段結束前調用stopMethodTracing函數。這兩個函數運行過程中將采集運行時間內該應用所有線程(注意,只能是Java線程)的函數執行情況,並將采集數據保存到/mnt/sdcard/下的一個文件中。開發者然后需要利用SDK中的Traceview工具來分析這些數據。
- 借助Android SDK中的DDMS工具。DDMS可采集系統中某個正在運行的進程的函數調用信息。對開發者而言,此方法適用於沒有目標應用源代碼的情況。DDMS工具中Traceview的使用如圖1-1所示。
圖1-1 DDMS中Traceview使用示意圖
點擊圖1-1中所示按鈕即可以采集目標進程的數據。當停止采集時,DDMS會自動觸發Traceview工具來瀏覽采集數據。
下面,我們通過一個示例程序向讀者介紹Debug類以及Traceview的使用。
1.2 Traceview示例分析
示例程序運行時的界面如圖1-2所示:
圖1-2 示例界面圖
圖1-2中:
- SystraceDemoStringAAA等字樣是TraceviewDemo程序啟動時ListView最初顯示的字符串。
- 當用戶點擊ListView中的某一項時,Traceview將計算對應項當前顯示的字符串的MD5值40次,然后用計算得到的MD5字符串替換該項之前顯示的內容。其效果如圖1-2中的“MD5值“箭頭所示。
該示例的關鍵代碼如圖1-3所示:
圖1-3示例代碼
由圖1-3可知:
- 左圖中,Debug類的startMethodTracing和stopMethodTracing分別在MainAcvtivity的構造方法和onDestroy函數中調用。
- onCreate函數中我們設置了第一個hotspot,即getStringToShow函數。它將解析一個XML文件,並將解析后的字符串保存到mListItem中以作為ListView的顯示內容。
- 右圖中,當用戶點擊ListView中的某個Item時,程序在onListItem中將計算MD5值40次,然后用計算結果做為被點擊項的新字符串顯示。generateMD5中的函數是本示例的第二個hotspot。
現在,我們用Traceview工具將測試結果文件TraceviewDemo.trace打開。
Traceview界面比較復雜,其UI划分為上下兩個面板,即Timeline Panel(時間線面板)和Profile Panel(分析面板)。圖1-4所示為Timeline Panel界面:
圖1-4 Traceview Timeline Panel示意圖
圖1-4中的Timeline Panel又可細分為左右兩個Pane:
- 左邊Pane顯示的是測試數據中所采集的線程信息。由圖1-4可知,本次測試數據采集了main線程,兩個Binder線程和其它系統輔助線程(例如GC線程等)的信息。
- 右邊Pane所示為時間線,時間線上是每個線程測試時間段內所涉及的函數調用信息。這些信息包括函數名、函數執行時間等。由圖1-4可知,main線程對應行的的內容非常豐富,而其他線程在這段時間內干得工作則要少得多。
- 另外,開發者可以在時間線Pane中移動時間線縱軸。縱軸上邊將顯示當前時間點中某線程正在執行的函數信息。
現在來看Traceview的Profile Panel界面,如圖1-5所示:
圖1-5 TraceviewProfile Panel界面
Profile Panel是Traceview的核心界面,其內涵非常豐富。它主要展示了某個線程(先在Timeline Panel中選擇線程)中各個函數調用的情況,包括CPU使用時間、調用次數等信息。而這些信息正是查找hotspot的關鍵依據。所以,對開發者而言,一定要了解Profile Panel中各列的含義。筆者總結了其中幾個重要列的作用,如表1-1所示:
表1-1 Profile Panel各列作用說明
列名 |
描述 |
Name |
該線程運行過程中所調用的函數名 |
Incl Cpu Time |
某函數占用的CPU時間,包含內部調用其它函數的CPU時間 |
Excl Cpu Time |
某函數占用的CPU時間,但不含內部調用其它函數所占用的CPU時間 |
Incl Real Time |
某函數運行的真實時間(以毫秒為單位),內含調用其它函數所占用的真實時間 |
Excl Real Time |
某函數運行的真實時間(以毫秒為單位),不含調用其它函數所占用的真實時間 |
Call+Recur Calls/Total |
某函數被調用次數以及遞歸調用占總調用次數的百分比 |
Cpu Time/Call |
某函數調用CPU時間與調用次數的比。相當於該函數平均執行時間 |
Real Time/Call |
同CPU Time/Call類似,只不過統計單位換成了真實時間 |
另外,每一個Time列還對應有一個用時間百分比來統計的列(如Incl Cpu Time列對應還有一個列名為Incl Cpu Time %的列,表示以時間百分比來統計的Incl Cpu Time)。
了解完Traceview的UI后,現在介紹如何利用Traceview來查找hotspot。
一般而言,hotspot包括兩種類型的函數:
- 一類是調用次數不多,但每次調用卻需要花費很長時間的函數。在示例代碼中,它就是hotspot 1。
- 一類是那些自身占用時間不長,但調用卻非常頻繁的函數。在示例代碼中,它就是hotspot 2。
首先,我們來查找hotspot 1。
在Profile Panel中,選擇按Cpu Time/Call進行降序排序(從上之下排列,每項的耗費時間由高到低),得到如圖1-6所示的結果:
圖1-6 按CPU Time/Call降序排列數據
圖1-6中:
- MainActivity.onCreate是應用程序中的函數,它耗時為4618.684。然后,點擊MainActivity.onCreate項,得到箭頭所示的小圖。
- 小圖中,Parents一行顯示的是MainActivity.onCreate的調用者,本例中它是performCreate函數。這部分代碼屬於Framework部分。Children行顯示的是MainActivity.onCreate調用的子函數。
- 在MainActivity.onCreate調用的子函數中,我們發現getStringsToShow在Incl Cpu Time %一列中占據了63.3%,它是onCreate子函數耗費時間最長的,而且Calls+Recur Calls/Total列顯示其調用次數為1,即它僅僅被調用一次了。這個函數是應用程序實現的,所以極有可能是一個潛在的Hotspot。
- 另外,由於筆者已經知道getStringsToShow是示例應用自己實現的函數,故在圖1-6的大圖中,可直接根據MainActivity.getStringsToShow花費了2921.913CPU時間這個信息來確定Hotspot就是它。
相對來說,類型1的hotspot比較好找,步驟是先按降序對時間項進行排列(可以是時間百分比、真實時間或CPU時間),然后查找耗費時間最多的函數。一般而言,先應對應用程序自己實現的函數進行排查,Framework的函數也有可能是hotspot,但主因一般還是在應用本身(例如設置復雜的界面,導致對應XML解析非常慢)。
現在,我們來看如何查找類型2的hotspot。
點擊Call/Recur Calls/Total列頭,使之按降序排列。關注點放在那些調用頻繁並且占用資源較多的函數。圖1-7為降序排列的結果圖。
圖1-7類型2 Hotspot查找過程示意之一
圖1-7所示的運行最頻繁的幾個函數中,我們發現了幾個懷疑點,由圖中的1和2箭頭標示。
- 結合代碼,箭頭1所指的函數在代碼中實際並不存在。這是因為代碼中直接訪問了內部類的私有成員,導致java編譯器在編譯時自動生成了這個函數。這個函數的調用次數非常多。所以,為了提高效率,我們可以修改內部類成員的訪問類型定義為public。不過,該函數的Incl Cpu Time並不高,只有3.2%。
- 同樣,箭頭2所指部分的函數調用次數也很多,達到了5888多次。不過它們占用的時間百分比只有0.9%。
第一次查找的潛在點被排除后,繼續瀏覽數據,得到如圖1-8所示的結果。
圖1-8 類型2 Hotspot查找過程示意之二
在圖1-8中:
- 紅框處有兩個重載的MyMD5.getHashString函數調用,它們各運行了368次,而且占用的CPU時間百分比達到了31.8%和53.2%。很顯然,這2處調用就有優化的余地,這就是我們所懷疑的hotspot2。
找到hotspot之后,開發者就需要結合代碼來進行對應的優化了。關於Java代碼優化,讀者可參考如下資料:http://developer.android.com/training/articles/perf-tips.html
總體而言,Hotspot的查找是一個細致的工作,需要開發者對目標程序的代碼,以及Traceview工具都比較熟悉才行。
1.3 Traceview小結
Traceview工具是Android平台應用程序性能分析的利器。不過筆者覺得它的UI還是有些復雜。並且使用時感覺流暢度不夠好。
Google官方關於Traceview的介紹可參考以下鏈接,不過其內容以及較久未更新了。http://developer.android.com/tools/debugging/debugging-tracing.html。
二Systrace介紹
2.1 Systrace簡介
Systrace是Android4.1中新增的性能數據采樣和分析工具。它可幫助開發者收集Android關鍵子系統(如surfaceflinger、WindowManagerService等Framework部分關鍵模塊、服務)的運行信息,從而幫助開發者更直觀的分析系統瓶頸,改進性能。
Systrace的功能包括跟蹤系統的I/O操作、內核工作隊列、CPU負載以及Android各個子系統的運行狀況等。在Android平台中,它主要由3部分組成:
- 內核部分:Systrace利用了Linux Kernel中的ftrace功能。所以,如果要使用Systrace的話,必須開啟kernel中和ftrace相關的模塊。
- 數據采集部分:Android定義了一個Trace類。應用程序可利用該類把統計信息輸出給ftrace。同時,Android還有一個atrace程序,它可以從ftrace中讀取統計信息然后交給數據分析工具來處理。
- 數據分析工具:Android提供一個systrace.py(python腳本文件,位於Android SDK目錄/tools/systrace中,其內部將調用atrace程序)用來配置數據采集的方式(如采集數據的標簽、輸出文件名等)和收集ftrace統計數據並生成一個結果網頁文件供用戶查看。
從本質上說,Systrace是對Linux Kernel中ftrace的封裝。應用進程需要利用Android提供的Trace類來使用Systrace。Android 4.1為系統中的幾個關鍵進程和模塊都添加了Systrace功能。以顯示系統中重要模塊Hwcomposer為例,其代碼中使用Systrace的方法如圖2-1所示:
圖2-1 Hwcomposer模塊Systrace使用示例
圖2-1中,應用程序只要通過三個宏就可使用Systrace了:
- 定義ATRACE_TAG:Hwcomposer使用了ATRACE_TAG_GRAPHICS,表示它和Graphics相關。
- ATRACE_INIT:用於統計某個變量使用的情況。下文將見到代碼中”VSYNC”的統計結果。
- ATRACE_CALL:用於統計函數的調用情況。
由於篇幅關系,關於Trace使用更多的信息請讀者閱讀frameworks/native/include/utils/Trace.h或者android.os.Trace類。下面,我們通過一個示例來展示Systrace的使用。
2.2 Systrace實例
首先,在PC機上運行如下命令以啟動Systrace,如圖2-2所示:
圖2-2 Systrace操作步驟
執行上述命令后,將得到一個名為trace.html的文件(trace.html是默認文件名,讀者也可在命令行中指定其他文件名)。通過瀏覽器打開此文件,結果如圖2-3所示:
圖 2-3 trace.html內容示意
圖2-3中所示的trace.html頁面內容和Traceview的Timeline Panel非常類似。圖中包含的內容如下:
- 由於在systrace.py中指定了-f -l和-i參數,Systrace將生成CPU頻率、負載和狀態相關的信息。它們為圖2-1中第一個紅框所示。由於筆者所測手機CPU為雙核,故圖中有CPU 0和CPU 1之分。為行文方便,筆者用CPU N來指代CPU的某個核。
- “CPU N“所示行對應於整個測試時間內,某個核上運行的進程信息。
- “CPU N C-State“所示行為整個測試時間內,某個CPU狀態的變化。C-State取值見表2-1。
- “CPU N Clock Frequency”所示行展示了某個CPU運行的頻率。通過點擊這一行的色塊可以查看某個時間點上CPU N的運行頻率。
- “cpufreq”:該行所示內容和CPU交互式頻率調節器(Interactive Governor)的工作有關。交互式CPU調節器驅動添加了對CPU頻率調節事件的跟蹤。感興趣的讀者不妨閱讀kernel中的include/trace/events/cpufreq_interactive.h文件以了解更多的信息。
圖2-1中,CPU信息以下的行就是通過Trace.h提供的宏而添加的統計信息,其中:
- VSYNC:這一行的統計信息來自於圖2-1中ATRACE_INIT宏的使用。在Hwcomposer代碼中,ATRACE_INIT宏被用於統計VSYNC[1]的Tick-Tack情況(即0,1,0,1交錯輸出)。VSYNC行顯示了每次Tick Tack的時間大概都在16ms左右。
- 由於Framework代碼也在顯示部分添加了ATRACE_INIT的使用,所以圖中com.example.systracedemo/com.example.systracedemo.MainActivity所示為應用程序占用顯示Buffer的Tick-Tack情況。如果使用時間超過16ms,將導致界面顯示遲滯等現象。
- SurfaceFlinger使用了ATRACE_CALL宏,故圖中SurfaceFlinger行展示了其函數調用的CPU耗時情況(如箭頭1所指,SurfaceFlinger中的onMessageReceived函數的運行信息)。
- 在圖2-1最下部的方框中,詳細顯示了當前鼠標在時間線中選擇的部分(即SurfaceFlinger中的onMessageReceived)的詳細信息。
表2-1所示為CPU狀態取值信息:
表2-1 CPU狀態
C-state |
描述 |
C-0 |
RUN MODE,運行模式。 |
C-1 |
STANDBY,就位模式,隨時准備投入運行 |
C-2 |
DORMANT,休眠狀態,被喚醒投入運行時有一定的延遲 |
C-3 |
SHUTDOWN,關閉狀態,需要有較長的延遲才能進入運行狀態,減少耗電 |
2.3 Systrace小結
總體來說,Systrace比Traceview用途更廣泛,它支持對CPU、Native進程甚至Kernel線程進行性能數據采樣,可幫助開發者對整個系統的性能情況進行一個詳盡的分析。不過其用法比Traceview要復雜,而且還需要對Kernel做一些配置調整。
Android官方對Systrace也有一些介紹,請讀者閱讀:
http://developer.android.com/tools/debugging/systrace.html
三Oprofile的使用
3.1 Oprofile簡介
Oprofile是另一個功能更強大的性能數據采集和分析工具,其工作原理如下:
- 它利用性能計數器(Performance Counter)或者定時器(針對kernel不支持性能計數器的情況),通過連續的采樣獲得統計數據,從而對內核和用戶空間進程進行性能分析。
- 以性能計數器為例,在系統運行過程中,當某個事件發生時,對應的性能計數器就會自加。當達到計數器的設定值時會產生一個中斷。Oprofile驅動利用這個中斷來進行采樣統計。通過獲取中斷發生時PC指針的值以及內核中保存運行的任務的信息等,並把它們轉化成對測評有用的數據。
- Oprofile包括內核驅動和用戶空間工具兩個部分,其中:
- 內核驅動實現了一個oprofilefs虛擬文件系統。它掛載到/dev/oprofile,用來向用戶空間報告數據和接收來自用戶空間的設置。它是用戶空間進程與內核通信的橋梁。驅動中還包括了與架構相關和通用的驅動,通過它們訪問性能計數器寄存器、收集數據后報告給用戶空間。守護進程用戶從內核接收數據並保存在磁盤上以備分析使用。
- 在用戶空間提供了兩個工具:oprofiled(作為守護進程在后台通過和/dev/oprofile交互以獲取驅動收集的數據)、opcontrol(用戶操作的控制工具,它通過讀寫oprofilefs來控制采樣相關的設置)。
Android默認提供了對Oprofile的支持,其組成包括:
- 代碼:位於exetrnal/oprofile中。不過,只有編譯類型為非user的系統才會使用它。
- 四個主要工具,即opcontrol,oprofiled、opreport和opimport。開發者只要使用opcontrol和opreport即可。
- 讀者應該熟練掌握opcontrol和oprofiled工具的作用,我們此處也總結了它們的用法:
- opcontrol:它用來控制采樣過程,比如采樣的開始和結束、采樣的事件類型和頻率等。其內部通過讀寫oprofilefs來實現。opcontrol的常用選項如表3-1所示:
表3-1 opcontrol常用選項
opcontrol選項 |
功能 |
--list-events |
列出當前CPU所支持的事件 |
--setup |
對測評進行設置,比如關閉舊的守護進程、掛載oprofilefs |
--vmlinux= |
設置將要分析的Android內核鏡像文件 |
--callgraph |
設置跟蹤函數調用的層數 |
--kernel-range=start,end |
內核二進制文件起始和結束的虛擬地址 |
--start/--stop |
開始/停止采樣 |
--event=name:count:unitmask:kernel:user |
設置對某事件進行采樣。 Name:事件的名字 Count:采樣時事件發生的次數Unitmask:事件的掩碼(CPU支持的事件以及掩碼見oprofile的文檔) Kernel:是否采樣內核事件 User:是否采樣用戶事件 |
- opreport:opreport是使用采樣數據生成報告的工具,可根據用戶要求生成不同的報告。一般用法是“opreport [options] [image]”,其中image指定報告需要顯示的程序的名字(指程序名字、共享庫名字和內核)。image參數可選。不指定它時,opreport將打印所有進程的報告結果。常用options如表3-2所示:
表3-2 opreport常用選項
opreprt選項 |
功能 |
-l |
顯示函數調用的符號名字 |
-g |
以調試的形式打印函數符號,包括函數所在文件及行數等。 |
-c |
顯示函數調用堆棧 |
-o |
報告輸出到指定文件 |
另外,Android提供了一個特別的工具opimport_pull。它可把采樣數據從手機中pull到PC上,並對數據進行一些簡單處理以供opreport使用。所以,在Android平台上,開發者只要使用opimport_pull了就可以了。
現在,我們來看Oprofile的使用實例。
3.2 Oprofile實例
Oprofile的使用大體可以分成以下三步:
- 內核加載oprofile驅動(如果該驅動靜態編譯到內核中,則可略過此步驟)。
- 配置采樣事件、然后進行采樣。
- 獲取報告、進行分析,針對分析結果進行改進。
下面分別來看這三個步驟:
3.2.1 Oprofile內核配置
如下所示為內核配置的示例,如圖3-1所示:
圖3-1 Oprofile內核配置示意
運行Oprofile需要root權限,所以目標設備中最好運行的是userdebug或者engineer版本的Android OS。
3.2.2 Oprofile用戶空間配置
Oprofile用戶空間配置的示例如圖3-2所示。假設當前目錄為Android源碼根目錄,並且已經初始化Android編譯環境(執行完畢build/envsetup.sh和lunch)。
圖3-2 Oprofile用戶空間配置示意
用戶空間的配置主要通過執行opcontrol命令來完成。而opcontrol內部是通過往oprofilefs傳遞對應的控制參數來完成的。例如圖3-2中“opcontrol --callgraph=16”命令也可通過“echo 16> /dev/oprofile/backtrace_depth”來實現。
3.2.3 結果分析
在上一步中,我們已經獲取了測評采樣的數據。現在,就可以使用它們來生成采樣報告了,方法如圖3-3所示:
圖3-3 oprofile生成采樣報告方法示意
圖3-4為報告的一部分內容:
圖3-4 Oprofile測評報告概要
圖3-4中,我們發現libc.so調用的采樣數為117299,排第4位。那么libc.so中哪個函數調用次數最多呢?開發者可通過如下命令獲取libc.so的更為詳細的信息。方法如圖3-5所示:
圖3-5 opreport使用示例
執行上述命令后的結果如圖3-6所示:
圖3-6 Oprofile關於libc的詳細結果
由圖3-6可知,memcpy()函數占用最多的CPU資源。所以可以考慮優化memcpy()。
此處,筆者針對Cortex-A9雙核SMP處理器,使用ARM匯編的方法對memcpy進行了優化。優化后的結果如圖3-7所示。對比圖3-6和圖3-7可明顯看出,優化后的memcpy對資源的占用降低了2.7個百分點。
圖3-7 優化memcpy()后的測試結果
3.3 Oprofile小結
在性能分析中,Oprofile無疑是一個使用最廣泛、功能最強大的測評工具。對於Android平台開發者來說,它可以采集和分析整個系統的運行狀態信息,對於分析查找系統瓶頸進而優化系統具有重大意義。
四 總結
性能調優向來是一件“高深莫測”的任務,但打破它們神秘面紗的工具就是上文所述的工具了。所以,對有志開展這方面工作的讀者而言,首要一步的工作就是先了解各個工具的作用及優缺點。
除了本文介紹的這三個工具外,Android系統還支持其他一些更有針對性的測試工具,例如用於測評系統整體功能的lmbench,lttng、測試系統啟動性能的bootchart、測試文件系統性能的iozone等。由於篇幅關系,筆者就不再一一介紹它們了。
[1]關於VSYNC的詳情,讀者可參考http://blog.csdn.net/innost/article/details/8272867,“Android Project Butter分析“一文。