剖析 GPU 轉譯工具會顯示在每個階段內,轉譯管道轉譯上一個影格所需的相對時間。此資訊可協助您找出管道中的瓶頸,讓您瞭解如何讓應用程式轉譯效能最佳化。
本頁將簡要說明每個管道階段所發生的情況,並探討可能造成效能瓶頸的問題。在閱讀本頁之前,請確保您熟悉剖析 GPU 轉譯一文中的資訊。此外,如要瞭解各階段如何搭配運作,建議您檢閱「轉譯管道的運作方式」。
視覺表示
剖析 GPU 轉譯工具會以彩色直方圖顯示階段及其相對時間。圖 1 顯示此類畫面的範例。
剖析 GPU 轉譯圖表中顯示的每個垂直長條都代表管道的一個階段,並以長條圖中的特定顏色醒目顯示。圖 2 顯示每個顏色代表的含意。
瞭解各項顏色代表含意後,您就可以指定應用程式的特定面向,嘗試將轉譯效能最佳化。
階段及其代表含意
本節說明各圖與圖 2 中的顏色對應情況,以及需要留意的瓶頸根源。
輸入處理
管道的輸入處理階段會計算應用程式處理輸入事件的時間長短。此指標表示應用程式因執行輸入事件回呼而呼叫執行程式碼的時間長度。
此區隔較大時
這個區域中的值如果過高,通常是因為輸入處理常式事件回呼發生的工作過多或太複雜。由於這些回呼都會在主要執行緒上發生,因此解決這個問題的方法主要是將工作直接最佳化處理,或是卸載到其他執行緒。
另外要注意的是,這個階段可能會出現 RecyclerView
捲動。RecyclerView
會在使用觸控事件時立即捲動,因此會加載或填入新的項目檢視畫面,所以一定要盡快執行這項作業。Traceview 或 Systrace 等剖析工具有助於進一步調查問題所在。
動畫
「動畫」階段會顯示評估該影格中執行的所有動畫所花費的時間。最常見的動畫為 ObjectAnimator
、ViewPropertyAnimator
和 轉場效果。
此區隔較大時
這個區域中的值如果過高,一般是因為動畫的某些屬性變更而執行工作所致。舉例來說,會捲動 ListView
或 RecyclerView
的快速滑過動畫,會因此加載及填入大量檢視畫面。
測量/版面配置
Android 系統為了在畫面中繪製檢視項目,會針對檢視區塊階層中的版面配置和檢視區塊執行兩項作業。
第一項作業是測量檢視項目。每個檢視區塊和版面配置都有用來描述自身顯示大小的資料。有些檢視區塊的大小固定,有些則會配合上層布局容器的大小來調整。
第二項作業是安排檢視項目的分布位置。系統在計算子項檢視畫面的大小後,就可以繼續調整版面配置,以及調整畫面大小和決定畫面的位置。
系統測量及配置的不只有檢視區塊,還有檢視區塊的上層結構,一直到根層級的檢視區塊為止。
此區隔較大時
如果應用程式在這個區域的每個影格都花費相當長的時間,通常是因為要配置版面的畫面較多,或是因為在層級中不正確之處發生了類似重複作業的問題。無論是哪一種情況,處理效能問題都需要改善檢視區塊階層的效能。
此外,加入 onLayout(boolean, int, int, int, int)
或 onMeasure(int, int)
的程式碼也可能導致效能問題。Traceview 和 Systrace 可協助您檢查呼叫堆疊,藉此找出程式碼可能發生的問題。
繪圖
繪製階段會將檢視區塊的轉譯作業 (例如繪製背景或文字) 轉換為一系列的原生繪製指令。系統會將這些指令擷取至顯示清單。
繪製列會記錄此影格所有必須在畫面中更新的檢視畫面,在完成指令擷取至顯示清單所花費的時間。測量時間會套用至新增到應用程式 UI 物件的任何程式碼。例如:onDraw()
、dispatchDraw()
,以及多種不同屬於 Drawable
子類別的 draw ()methods
。
此區隔較大時
簡單來說,您可以將此指標視為顯示每個無效檢視表對 onDraw()
執行所有呼叫所需的時間。這個測量結果包括指派繪圖指令稚子項,以及可能顯示的可繪項目所花費的時間。因此,如果發現此長條突然大幅提高,就可能代表有大量的檢視畫面突然失效。失效就必須重新產生畫面檢視的顯示清單。此外,少數自訂檢視畫面如果在其 onDraw()
方法中有非常複雜的邏輯,也可能導致所需時間過長。
同步處理/上傳
「同步處理和上傳」指標代表在目前影格中,將點陣圖物件從 CPU 記憶體轉移到 GPU 記憶體所花費的時間。
由於處理器不同,CPU 和 GPU 也有不同的專屬處理用 RAM 區域。在 Android 上繪製點陣圖時,系統會先將點陣圖轉移到 GPU 記憶體,這樣 GPU 就可以在螢幕上顯示點陣圖。除非已從 GPU 材質快取中清除材質,否則 GPU 會快取點陣圖,避免系統再次傳輸資料。
附註:在 Lollipop 裝置中,此階段為紫色。
此區隔較大時
影格的所有資源都必須位於 GPU 記憶體中,才能用於繪製影格。這表示此指標的值如果很高,就可能代表有大量的小型資源載入,或少量的大型資源。常見的情況是,應用程式顯示的單一點陣圖與畫面尺寸相近。另一個情況是應用程式顯示大量縮圖。
如要縮減此列,您可以採用不同的技巧,例如:
- 確保點陣圖解析度不會比顯示大小還要大。舉例來說,應用程式不應將 1024x1024 圖片顯示為 48x48 圖片。
-
利用
prepareToDraw()
,在下次同步處理階段之前以非同步方式預先上傳點陣圖。
發出指令
「發出指令」區段代表為了在畫面中繪製顯示清單而發出所有指令的所需時間。
系統為了能在畫面中繪製顯示清單,會傳送必要的指令至 GPU。一般而言,此操作是經由 OpenGL ES API 執行。
這個程序需要一段時間才能完成,因為系統會先對每個指令執行最終轉換和剪輯,然後再將指令傳送至 GPU。然後,GPU 端會進行額外的工作,計算最終的指令。這些指令包含最終轉換和其他剪輯。
此區隔較大時
在這個階段中,系統會直接測量在特定影格中轉譯的複雜度和數量。舉例來說,如果有多個繪圖作業,特別是每個繪圖基元的固有成本都很低的時候,這可能會增加此所需時間。例如:
Kotlin
for (i in 0 until 1000) { canvas.drawPoint() }
Java
for (int i = 0; i < 1000; i++) { canvas.drawPoint() }
的發出所需成本更高:
Kotlin
canvas.drawPoints(thousandPointArray)
Java
canvas.drawPoints(thousandPointArray);
發出指令和實際繪製顯示清單並非總是有 1:1 的關聯。與「發出指令」不同,後者會擷取繪圖指令傳送至 GPU 的時間,但「繪圖」指標則代表擷取向顯示清單發出指令所需的時間。
這種差異是因為系統會盡可能快取顯示清單所致。因此會有捲動、轉換或動畫時系統必須重新傳送顯示清單,但不需要實際重新建立 (重新擷取繪圖指令)。因此,您會發現「發出指令」列很高,但「繪圖指令」列不高。
程序/切換緩衝區
Android 將所有的顯示清單提交至 GPU 後,系統會發出最後一個指令,告知圖形驅動程式已使用目前影格完成操作。此時,驅動程式終於可以在畫面中顯示更新畫面。
此區隔較大時
請務必瞭解 GPU 可和 CPU 並行運作。Android 系統會向 GPU 發出繪圖指令,然後繼續執行下一個工作。GPU 會從佇列中讀取這些繪圖指令,然後進行處理。
如果 CPU 發出指令的速度比 GPU 更快,則處理器之間的通訊佇列可能會滿。如果發生這種情況,CPU 會封鎖,然後等待佇列中有空間再放置下一個指令。此完整佇列狀態通常會在「切換緩衝區」階段發生,因為在這個時間點,系統已提交完整的影格指令。
處理這個問題的關鍵在於能否減少 GPU 上運作的複雜度,做法和「發出指令」階段的方式類似。
其他
除了轉譯系統執行作業所需要的時間之外,系統還會額外進行一組在主要執行緒上處理的工作,而這與轉譯並無關係。此工作花費的時間會回報為其他時間。其他時間通常是指可能在 UI 執行緒之間,連續影格之間的轉譯工作。
此區隔較大時
如果此值偏高,就表示應用程式可能有回呼、意圖或其他應在另一個執行緒上進行的工作。方法追蹤記錄或 Systrace 等工具可協助您瞭解在主執行緒上執行的工作。此資訊可協助您進一步改善效能。