使用剖析 GPU 轉譯進行分析

剖析 GPU 轉譯工具會顯示在每個階段內,轉譯管道轉譯上一個影格所需的相對時間。此資訊可協助您找出管道中的瓶頸,讓您瞭解如何讓應用程式轉譯效能最佳化。

本頁將簡要說明每個管道階段所發生的情況,並探討可能造成效能瓶頸的問題。在閱讀本頁之前,請確保您熟悉剖析 GPU 轉譯速度一文中的資訊。此外,如要瞭解各階段如何搭配運作,建議您檢閱「轉譯管道的運作方式」。

視覺表示

剖析 GPU 轉譯工具會以彩色直方圖顯示階段及其相對時間。圖 1 顯示此類畫面的範例。

剖析 GPU 轉譯圖表
圖 1. 剖析 GPU 轉譯圖表

剖析 GPU 轉譯圖表中顯示的每個垂直長條都代表管道的一個階段,並以長條圖中的特定顏色醒目顯示。圖 2 顯示每個顏色代表的含意。

剖析 GPU 轉譯圖表圖例
圖 2. 剖析 GPU 轉譯圖表圖例

瞭解各項顏色代表含意後,您就可以指定應用程式的特定面向,嘗試將轉譯效能最佳化。

階段及其代表含意

本節說明各階段發生的情況,以及需要留意的瓶頸根源。

輸入處理

管道的輸入處理階段會計算應用程式處理輸入事件的時間長短。此指標表示應用程式因執行輸入事件回呼而呼叫執行程式碼的時間長度。

此區隔較大時

這個區域中的值如果過高,通常是因為輸入處理常式事件回呼發生的工作過多或太複雜。由於這些回呼都會在主要執行緒上發生,因此解決這個問題的方法主要是將工作直接最佳化處理,或是卸載到其他執行緒。

這個階段也可能會出現捲動 LazyColumnLazyRow。一旦使用者觸控符合捲動條件,延遲清單就會使用觸控事件,動態組合及配置項目。如果應用程式會回應捲動位置變更來執行自訂工作,請務必盡快完成這項作業,以免影格掉落。Android Studio 中的 CPU 分析器或 Perfetto 等剖析工具有助於進一步調查問題所在。詳情請參閱「系統追蹤總覽」一文。

動畫

「動畫」階段會顯示評估該影格中執行的所有動畫狀態所花費的時間。Compose 中常見的動畫 API 包括 animate*AsStateTransitionAnimatable。此外,Recomposer 會在這個階段執行,處理快照狀態變更並更新組合。這表示重新組合的額外負荷通常會直接出現在動畫階段。

如果是 Jetpack Compose UI,請加入 Compose 執行階段追蹤程式庫,即可查看詳細的組合追蹤記錄和系統事件。

此區隔較大時

這個區域中的值如果過高,一般是因為動畫驅動的狀態變更而執行工作所致。舉例來說,會捲動 LazyColumnLazyRow 的快速滑過動畫,會導致系統快速組合、測量及分配新的清單項目。

評估

如要在畫面上繪製可組合項,Android 會在 UI 樹狀結構的版面配置節點中執行三個階段

第一項作業是測量版面配置節點。每個可組合項都有特定限制和修飾符,用於描述物件在螢幕上的大小限制。有些可組合函式的大小固定,有些則會配合上層布局容器傳遞的限制條件來調整。

第二項作業是放置版面配置節點。Compose 在測量階段計算子項節點的大小後,即可繼續進行放置階段,在畫面上調整版面配置節點的大小和位置。

系統一律會執行這項單次傳遞版面配置,以提高效率。可組合版面配置失效時,Compose 會測量該特定節點,且只有在子項變更大小或限制時,才會將版面配置更新向上傳播至父項階層。

此區隔較大時

如果這個區域的區隔偏大,表示應用程式在版面配置階段花費太多時間,包括定位及判斷版面配置節點的大小。這些作業包括執行可組合項的測量和放置修飾符,如果版面配置樹狀結構過於複雜,可能會延遲影格準備作業。在這些情況下,如要解決效能問題,請為 Compose 應用程式建立基準,並遵循效能最佳做法

使用 Android Studio 或 Perfetto 中的 CPU 分析器檢查版面配置傳遞,並找出瓶頸。詳情請參閱「系統追蹤總覽」一文。

繪圖

繪製階段會將轉譯作業 (例如繪製背景、形狀或文字) 轉換為一系列的原生繪製指令。系統會將這些指令擷取至顯示清單,供 GPU 執行。

繪製列會記錄此影格所有必須在畫面中更新的版面配置節點,在完成指令擷取至顯示清單所花費的時間。測量時間也適用於您在繪圖修飾符或 Canvas 可組合項中可能有的任何自訂繪圖邏輯

此區隔較大時

簡單來說,您可以將此指標視為顯示每個無效版面配置節點執行所有繪圖指令所需的時間。這項測量結果包括指派這些指令給子節點和向量可繪項目所花費的時間。因此,如果發現此長條突然大幅提高,就可能代表有大量的可組合函式突然失效。失效就必須重新執行繪圖指令,並重新產生版面配置節點的顯示清單。此外,少數自訂可組合函式或畫布如果在 DrawScope 實作中含有非常複雜的邏輯,也可能導致所需時間過長。

此外,Compose 通常會在平台視為「繪製」階段的期間,處理其內部測量和版面配置傳遞作業。因此,Draw 欄位值偏高可能是因為內部測量/版面配置作業耗費過多資源或過於頻繁,而非單純的繪圖指令。如有疑問,請擷取 Perfetto 追蹤記錄,查看額外負荷是否源自繪圖常式或 Compose 測量和版面配置傳遞。

上傳

「上傳」指標代表在目前影格中,將點陣圖物件從 CPU 記憶體轉移到 GPU 記憶體所花費的時間。

由於處理器不同,CPU 和 GPU 也有不同的專屬處理用 RAM 區域。在 Android 上繪製點陣圖時,系統會先將點陣圖轉移到 GPU 記憶體,這樣 GPU 就可以在螢幕上顯示點陣圖。除非已從 GPU 紋理快取中清除紋理,否則 GPU 會快取點陣圖,避免系統再次傳輸資料。

附註:在 Lollipop 裝置中,此階段為紫色。

此區隔較大時

影格的所有資源都必須位於 GPU 記憶體中,才能用於繪製影格。這表示此指標的值如果很高,就可能代表有大量的小型資源載入,或少量的大型資源。常見的情況是,應用程式顯示的單一點陣圖與畫面尺寸相近。另一個情況是應用程式顯示大量縮圖。

如要縮減此列,您可以採用不同的技巧,例如:

  • 確保點陣圖解析度不會比顯示大小還要大。舉例來說,請避免將 1024x1024 圖片顯示為 48x48 圖片。
  • 利用 Coil 等現代程式庫,在下次同步處理階段之前以非同步方式預先上傳點陣圖。

發出指令

「發出指令」區段代表為了在畫面中繪製顯示清單而發出所有指令的所需時間。

系統為了能在畫面中繪製顯示清單,會傳送必要的指令至 GPU。一般而言,此操作是經由 OpenGL ES API 執行。

這個程序需要一段時間才能完成,因為系統會先對每個指令執行最終轉換和剪輯,然後再將指令傳送至 GPU。然後,GPU 端會進行額外的工作,計算最終的指令。這些指令包含最終轉換和其他剪輯。

此區隔較大時

在這個階段中,系統會直接測量在特定影格中轉譯的複雜度和數量。舉例來說,如果有多個繪圖作業,特別是每個繪圖基元的固有成本都很低的時候,這可能會增加此所需時間。例如:

for (i in 0 until 1000) {
    canvas.drawPoint()
}

的發出所需成本更高:

canvas.drawPoints(thousandPointArray)

發出指令和實際繪製顯示清單並非總是有 1:1 的關聯。與「發出指令」列不同,後者會擷取繪圖指令傳送至 GPU 的時間,但「繪圖」指標則代表擷取向顯示清單發出指令所需的時間。

這種差異是因為系統會盡可能快取顯示清單所致。因此會有捲動、轉換或動畫時系統必須重新傳送顯示清單,但不需要實際重新建立 (重新擷取繪圖指令)。因此,您會發現「發出指令」列很高,但「繪圖指令」列不高。

切換緩衝區

Android 將顯示清單提交至 GPU 後,系統會發出最後一個指令,告知圖形驅動程式已使用目前影格完成操作。此時,驅動程式終於可以在畫面中顯示更新畫面。

此區隔較大時

請務必瞭解 GPU 可和 CPU 並行運作。Android 系統會向 GPU 發出繪圖指令,然後繼續執行下一個工作。GPU 會從佇列中讀取這些繪圖指令,然後進行處理。

如果 CPU 發出指令的速度比 GPU 更快,則處理器之間的通訊佇列可能會滿。如果發生這種情況,CPU 會封鎖,然後等待佇列中有空間再放置下一個指令。此完整佇列狀態通常會在「切換緩衝區」階段發生,因為在這個時間點,系統已提交完整的影格指令。

處理這個問題的關鍵在於能否減少 GPU 上運作的複雜度,做法和發出指令階段的方式類似。

其他

除了轉譯系統執行作業所需要的時間之外,系統還會額外進行一組在主要執行緒上處理的工作,而這與轉譯並無關係。此工作花費的時間會回報為其他時間。其他時間通常是指可能在 UI 執行緒之間,連續影格之間的轉譯工作。

此區隔較大時

如果此值偏高,就表示應用程式可能有回呼、意圖或其他應在另一個執行緒上進行的工作。Android Studio 中的 CPU 分析器或 Perfetto 等工具,可協助您瞭解在主執行緒上執行的工作。此資訊可協助您進一步改善效能。詳情請參閱「系統追蹤總覽」一文。