Android系統效能調優工具介紹

weixin_34377065發表於2016-01-16

經作者授權,發表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分析“一文。

相關文章