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