[轉] Android 效能分析案例

雨知發表於2013-09-25

Android 系統的一個工程師(Romain Guy)針對Falcon Pro  應用,撰寫了一個Android效能分析的文章。該文章介紹瞭如何分析一個應用哪裡出現了效能瓶頸,導致該應用使用起來不流暢。找到原因、並修復問題。即使沒有應用原始碼也能分析出問題大概根源。

需要的工具
工具很簡單,只需要Android 4.2 SDK即可

聊聊效能
Android 4.1 的Project Butter關注於效能問題,並引入了一些新的效能分析工具、例如 systrace。雖然Android 4.2沒有提供如systrace一樣強大的分析工具,但也提供了一些新的得心應手的小工具。在該文章內會介紹一個常用的工具,其他有趣的內容等待讀者自行探索了。效能分析經常是個複雜的任務,需要很豐富的經驗和對相關領域的深入瞭解,比如 硬體、API等。有了Android SDK提供的工具,分析起來應該會相對簡單一些。

先確認疑點
下圖是Falcon Pro時間軸介面截圖,在使用的時候滑動感覺有點卡,有丟幀現象。

Falcon Pro 的時間軸介面

Falcon Pro 的時間軸介面

在分析效能問題的時候,非常重要的一點就是用測量工具來證實您的猜疑。儘管 Falcon Pro在Nexus 4上存在明顯的丟幀現象,我還是需要用工具確認下的。因此,我在Nexus 7上也安裝了該應用,N7比N4提供

了不同的分析工具。在N7上執行該應用,還是存在丟幀現象,甚至還更嚴重。為了實際測量下,我決定使用在Android 4.1引入的一個新功能 — GPU呈現模式分析 — 來分析下。您可以在設定中的“開發者選項”介面找到該工具。

啟用 GPU呈現模式分析

啟用 GPU呈現模式分析

什麼!? 在Android 4.2上您沒找到“開發者選項”? 哦,別急,只需要進入“關於手機”或者“關於平板電腦”介面,在最下面的“版本號”一行上連續點選7下即可。

啟用該選項後,系統會保留每個介面最後128幀繪製的時間資訊。目前在使用該工具前,您需要先幹掉要分析的應用 — 以後的Android版本將會去除該前置條件。

分析方法:除非有註明,否則這裡的每次分析測量都是通過慢慢的上下滑動該應用的時間軸介面,最多滾動一個List項。

在啟動該應用並滾動幾下時間軸介面,然後執行如下命令:

$ adb shell dumpsys gfxinfo com.jv.falcon.pro

在顯示的結果中,會看到一個標題為 “ Profile data in ms”的區域,下面包含了3列數字。 為了圖形化顯示,您可以把這些資料複製到Excel表格中,來顯示一個累積柱狀圖。如下圖:

每幀繪製的累計柱狀圖

每幀繪製的累計柱狀圖

表格檔案可以到這裡下載或者線上檢視(Google Doc 可能需要特殊工具)

每列資料顯示了渲染每一幀需要的時間:

  1. Draw 是在Java中建立顯示列表所需要的時間。這個值顯示了執行繪圖函式用了多長時間,比如View.onDraw(Canvas)。
  2. Process 是Android 2D引擎渲染顯示列表所需要的時間。在介面中View數目越多,則有越多的繪製命令需要執行。
  3. Execute 是把一幀資料送到螢幕上排版顯示的時間,這個時間通常比較小。

注意:要流暢的執行60幀/秒, 則需要每幀的處理時間不超過16ms。

關於Execute:如果Execute時間過長,說明您的應用跑到圖形管道(graphics pipeline)前頭去了。

Android同時最多可以有3個緩衝區,如果您需要另外一個,在緩衝區沒有釋放前應用會被阻塞住。有兩種原因會導致這種情況。第一個是您的應用在Dalvik中繪製的很快,但是在GPU中顯示需要的時間比較多。

第二個是應用使用了過多的時間來顯示前面幾幀動畫,當渲染管道填滿後,如果動畫不執行完則不再接受新的請求。在未來的Android版本中會改進這個問題。

上圖明顯說明了我的猜疑:該應用大部分情況下表現良好,偶爾會有幾幀丟失。

詳細的瞅瞅

該資料目前只是表明應用程式有時候需要較長的時間來繪製介面,但是還沒有告訴您是啥原因導致的。幀率還有可能被沒有計劃或者錯誤計劃的幀影響。例如,如果一個應用的繪製時間總是小於16ms,但是有時在兩幀之間做長時操作,將會引起丟幀現象。

Systrace 是一個簡易的工具來檢查Falcon Pro中是否存在該問題。該工具是一個系統分析工具。分析結果比較精確,讓你可以知道整個系統在幹什麼,當然也包含您的應用。

要使用Systrace,進入到“開發者選項”介面,選擇“啟用跟蹤”。在顯示的對話方塊中來選擇您需要跟蹤的型別。 這裡我們只對Graphics 和 View 感興趣。

啟用Systrace

啟用Systrace

注意:不要忘記關閉“GPU呈現模式分析”選項。

要使用Systrace,開啟命令列,執行 tools/systrace目錄中的systrace.py:

$ ./systrace.py

需要安裝Python,在windows下執行請參考這裡

如果您沒有安裝Python,也不想安裝,則可以通過Eclipse 中的ADT來使用該功能,詳情見下圖(使用的是ADT 21)

在ADT中使用systrace

在ADT中使用systrace

該工具預設會捕獲5秒的事件。我只是簡單的上下滾動下時間軸。跟蹤結果是一個獨立的網頁檔案,可以到這裡下載該檔案。

提示:要導航systrace文件,使用WASD鍵即可縮放和移動(找到打CS的感覺了吧!)W會放大當前滑鼠焦點。

systrace文件顯示了一些非常有趣的資訊。例如,顯示了一個Process被分配到那個CPU去執行了。如果您放大到最後一行(10440: m.jv.falcon.pro),您可以看到該應用正在做什麼。如果您點選每個performTraversals 塊,則可以看到應用繪製一幀需要多少時間。

大部分的performTraversals 都低於16ms,有些需要較多的時間,這些資料證實了前面的猜想(移動到935ms處可以看到這樣的一個塊)。

更有意思的是,您可以發現有時候該應用沒有安排繪製操作而導致丟幀。定位到270ms處有個deliverInputEvent 佔用了25ms。這個塊說明該應用使用了25ms來處理一個點選事件。由於該應用使用了一個ListView,很有可能問題就出在這個ListView的Adapter上。

Systrace不僅可以幫助我們發現一個應用需要多少時間來繪製每幀,同時還可以幫助我們發現其他潛在的效能瓶頸所在。是個非常有用的工具,但也有限。該工具只提供了高層面的資料,我們需要依靠其他工具

來確認問題到底出在何處。

圖形化過度繪製

有多種原因可以導致繪製效能低下,但是在Android中一個常見的原因就是“過度繪製”。每當應用讓系統在其他內容上繪製內容的時候就會導致過度繪製。想象一個簡單的場景:一個帶有白色背景的視窗,在該視窗上有個按鈕。當系統繪製該按鈕的時候是在白色背景上繪製的,這就是過度繪製。過度繪製 無法避免,但是如果太多了 則會引起效能問題。裝置的記憶體頻寬是有限的,當過度繪製導致應用需要更多的頻寬(超過了可用頻寬)的時候效能就會降低。頻寬的限制每個裝置都可能是不一樣的。

一個好的參考目標就是控制過度繪製為2X;這說明您可以繪製一次螢幕,然後在上面繪製最多2次內容,

一共繪製每個畫素3次。

過度繪製通常也說明了額外的問題:太多的View了、複雜的佈局、較長的inflation 時間等。

Android提供了3種工具來分析和解決過度繪製的問題: Hierarchy Viewer, Tracer for OpenGL和顯示GPU過度繪製。前兩個工具可以在ADT中找到,或者獨立的monitor 工具(位於android-sdk-windows\tools\monitor.bat)。第三個工具是“開發者選項”中的一個功能。

顯示GPU過度繪製

顯示GPU過度繪製

顯示GPU過度繪製 在螢幕上繪製不同的顏色來表明過度繪製的情況。啟用該選項(另外不要忘記了先幹掉您的應用):

過度繪製情況的好壞通過顏色來表示,從藍色、綠色、淡紅色到紅色 ,分別代表從好到壞(1x過度繪製、2x過度繪製、3x過度繪製和超過4x過度繪製)。少量的淡紅色可以接受,二紅色就是實現有問題,需要解決。沒有顏色表明沒有過度繪製。

在檢視 Falcon Pro的過度繪製之前,先看看系統設定介面的過度繪製如何。如下圖:

設定介面的過度繪製圖形化顯示

設定介面的過度繪製圖形化顯示

只有一兩個淡紅色,其他都是良好的。

關於透明畫素:仔細的看看前面的截圖。每個圖示上都是藍色。這說明透明的圖示也屬於過度繪製。透明的圖示也需要通過GPU處理,需要消耗資源。Android使用Layer和9-patches來優化透明畫素的繪製,所以您只需要關注Bitmap中的透明畫素。

過度繪製和GPU:目前有兩種移動GPU架構。一種使用deferred rendering可以稍微優化過度繪製;另外一種使用immediate rendering,無法優化過度繪製。關於這兩種架構的詳細優缺點請自行Google。

現在來看看 Falcon Pro的過度繪製情況:

Falcon Pro 時間軸介面的過度繪製

Falcon Pro 時間軸介面的過度繪製

哇哦,很多紅色哦!List的背景為綠色也是非常有趣的。這表明在該應用還沒開始繪製內容的時候已經有2x的過度繪製了。這個問題通常都是有多個全屏背景導致的,修復起了還是非常簡單的。另外在新的ADThint工具中也提供了對過度繪製的提示。

刪除不相關的層級

要減少過度繪製,我們需要先了解其產生的根源。這就要用到Hierarchy Viewer 和Tracer for OpenGL工具了。 Hierarchy Viewer可以獨立使用也可以在ADT中使用,可以用來分析一個介面的View層級結構。在解決佈局問題的時候非常有用,但對於定位效能問題也有一定的幫助。

重要事項: Hierarchy Viewer預設只能在非安全裝置上使用,也就是工程樣機或者模擬器。要是實際的裝置中使用 Hierarchy Viewer,您需要在應用中新增一個開源庫 ViewServer

開啟ADT的Hierarchy Viewer檢視,然後選擇Windows tab。粗體高亮的視窗就是當前裝置最上面的視窗,通常情況下就是您要分析的佈局。點選選中該項,然後點選工具條上的“Load”按鈕(看起來像一個藍色方塊的樹)。載入View樹可能需要較長的時間,所以請耐心點。 當載入完後,您將會看到一個和下圖差不多的介面:

Hierarchy Viewer檢視

Hierarchy Viewer檢視

在工具中可以把該圖匯出為一個Photoshop文件。只需要點選第二個按鈕即可。

Photoshop文件把該應用中的每個View顯示在一個圖層上。每個圖層根據 View.getVisibility()的返回值標記為可見或者不可見。每個圖層通過View的屬性android:id或者類名來命名。通過檢視這些圖層,很快就可以發現至少一處過度繪製:多個全屏背景。第一個是名稱為 DecorView圖層的背景,該View是由Android系統生成的,裡面包含了在主題(theme)中設定的背景。該背景在應用中是不可見的,所以可以把它刪除掉。

從DecorView 往上看,可以看到一個LinearLayout 包含另外一個漸變全屏背景。這個背景和DecorView的情況一樣,也可以刪除掉。現在唯一可見的背景是一個名字為id/tweet_list_container的View提供的。

在Photoshop中檢視到處的Hierarchy Viewer

在Photoshop中檢視到處的Hierarchy Viewer

刪除視窗背景:主題中定義的背景,是系統啟動應用的時候來建立預覽視窗的。除非您的應用是透明的,否則不要設定為Null。可以設定為您需要的顏色或者圖片。或者在onCreate() 中呼叫getWindow().setBackgroundDrawable(null)來刪除。

進一步減少過度繪製

Photoshop文件可以幫助理解應用是如何建立的,但是用了查詢更小的過度繪製則比較困難。現在該Tracer for OpenGL出場了。開啟ADT中“Tracer for OpenGL”透檢視,然後點選工具條上的箭頭圖示。

使用OpenGL Traces

使用OpenGL Traces

輸入您應用的包名稱和啟動Activity的名稱,然後選擇一個儲存的地址後點選“Trace”按鈕。

提示: OpenGL traces 檔案可能會非常大並且捕獲起了非常慢。為了減小檔案並加速捕獲,可以把DataCollection Ooptions 中的選項都取消掉。

Activity 名稱:當啟動一個Activity的時候 在logcat中會顯該Activity的名稱。

當應用啟動並執行時,開啟前面兩個選項:
Collect Framebuffer contents on eglSwapBuffers()
Collect Framebuffer contents on glDraw*()

第一個選項用來快速定位對應的幀,第二個選項可以用來檢視每幀通過每個繪製命令繪製的。第二個選項是解決過度繪製問題的關鍵。

啟動這兩個選項後,我開始滾動時間軸介面。現在將會需要比較長的時間來捕獲每幀資料,由於時間比較長,建議您下載這個資料吧。在 Tracer for OpenGL 中點選工具條上第一個按鈕可以開啟該檔案。

開啟後可以看到每個傳送給GPU的GL命令。如果您下載了我提供的分析檔案,找到21幀。當選擇一幀後,您可以在Frame Summary 介面中檢視該幀的介面。還可以點選繪製命令(藍色高亮顯示),在Details介面中檢視當前的狀態。

組織結構:GL命令通過View來分組顯示。重建了在 Hierarchy Viewer或者您的佈局XML檔案中的View樹。這樣非常方便檢視那個Veiw執行了什麼操作。

分別點選前3個繪製命令,可以發現在前面Photoshop中發現的問題– 全屏的背景繪製了3次。

進一步向下查詢,可以發現更多的可優化之處。當繪製一條推特訊息(一個List Item)的時候,使用一個ImageView來繪製頭像。這個ImageView先繪製了一個背景,然後在其上繪製了頭像。如下圖:

如果您觀察仔細的話會發現,這個頭像的背景只是當做頭像的邊框來用。這樣頭像本身和下面被蓋住的背景就照成了過度繪製。這個背景基本上全部被頭像蓋住了。

有一種簡單的修復該問題的方法,把這個9-patch格式的背景圖中間拉伸部分設定為透明的。Android 2D渲染引擎會優化9-patch圖中的透明畫素。這個簡單的修改可以消除頭像上的過度繪製。

有意思的是,在訊息中的圖片上也使用了同樣的背景。頭像尺寸較小,這點過度繪製可以不在乎,但是訊息中的圖片可能非常大,這個地方的過度繪製就很嚴重了。修復方式同上。

將來的願景:我希望將來Android 2D渲染引擎可以自動探測過度繪製,並且在繪製的時候優化。當前Android開發團隊有一些創新的想法,但是還無法確定何時會實現該功能。和內建的GPU優化一樣,該功能只適合完全不透明的場景。

縮短View層級結構

現在過度繪製的問題已經解決了,再次回到Hierarchy Viewer介面。通過檢視這個樹形結構,我們可以嘗試發現一些非必須的View。刪除這些View(特別是ViewGroup)不僅僅能提高幀率還能降低對記憶體的消耗、同時還能加速應用的啟動 等等。反正就是好處無窮。

快速掃描一眼 Falcon Pro的Hierarchy Viewer結構,就可以發現幾個ViewGroup中只包含了一個View。這種ViewGroup通常是非必須的,並且很容易移除。在下圖中至少有2個這種節點應該移除。

另外這個樹上還有很多其他View可以移除。例如,每個推特訊息包含一個名字為 id/listElementBottom的RelativeLayout 。該佈局包含了訊息作者的名字、推特@操作、釋出的時間以及一個圖示。名字和@操作是兩個TextView,這兩個TextView可以用一個來實現,分別設定不同的Style即可。後面的時間和圖片分別用TextView和ImageView來實現,同樣可以只用一個TextView並結合TextView組合圖示來實現。

左邊的劃入選單使用了幾個LinearLayout+TextView+ImageView 來顯示圖示和文字。每個組合都可以通過單一的TextView來實現。

如何縮短UI層級結構:在2009的Google IO中詳細介紹了一些方法,演講的標題為Turbo-charge your UI。http://www.google.com/events/io/2009/sessions/TurboChargeUiAndroidFast.html

關於輸入事件

還記得前面我們檢視Systrace發現的那個點選事件嗎?現在是時候來看看這個問題了,traceview則是我們的最佳工具。

traceview是一個Dalvik分析器,該分析器記錄了應用中每個函式的執行時間。通過ADT(或者獨立工具)中的DDMS透檢視來使用該工具,在Devices視窗中選擇您的應用程式,然後點選“Start method profiling”按鈕(3個箭頭帶個紅點的那個鈕)。

啟用跟蹤後,我上下滾動了幾下時間軸,然後再次點選下該按鈕來停止跟蹤。您可以從這裡下載我的trace檔案。 結果如下圖所示:

點選#21條目,ViewRootImpl.draw()顯示了繪製的時間。表格的最後一列顯示了該函式執行平均需要的時間。如果您仔細的看看上面的時間軸,可以發現在連續的幀之間的缺口。

有種很簡單的方式來檢視這些缺口正在幹什麼,縮放到缺口起始位置然後點選你可以發現的最大色塊。然後跟隨父節點一直查詢到某個您熟悉的函式為止。 對於我而言,我跟隨一個Pattern.compileImpl函式呼叫(平均耗時0.5ms)一直到DBListAdapter.bindView。很顯然該應用一直重新編譯同一個正規表示式,在滾動時間軸的時候每當一個新的ListItem顯示的時候,就會重新編譯一次。Traceview 指出bindView消耗了38ms時間,而56%的時間都是在解析HTML文字。這表明該任務可以在後臺執行緒中執行而不應該在UI主執行緒中。當然了,正規表示式無需每次都重新編譯。

該你了

最後一個分析作為一個練習吧。該應用使用了左右兩個滑動選單。通過“顯示GPU過度繪製”工具發現在滑動的時候過度繪製非常嚴重,我使用“ Tracer for OpenGL ”捕獲了一些滑動的資料。下載這個資料檔案, 看看您是否可以發現是啥原因導致過度繪製的(定位到 #34幀)

提示:該應用應該通過View.setLayerType() 函式來啟用硬體圖層繪製。通過更明智的使用9-patch圖片可以優化一些背景導致的過度繪製。剪裁也比較有用。最終,可以通過把傳遞給setLayerType() 的Paint上設定一個ColorFilter來移除最後一個繪製命令。

這篇文章中顯示了各種可以用來分析優化應用效能的工具。如果詳盡描述每個工具則可以寫成一本書了,所以詳細的資訊請參考Android官方網站的相關文件和Google IO上的各種Android演講。

注:如果你沒有使用eclipse和ADT,則在Android SDK中也包含了一個獨立的monitor工具,最新版本的工具也是基於Eclipse RPC開發的,通過如下檔案執行 android-sdk-windows\tools\monitor.bat

轉自: http://blog.chengyunfeng.com/?p=458#ixzz2frrheoSi

相關文章