Flutter 性能分析
你將學到
Flutter 的目標是提供 60 幀每秒 (fps) 的性能,或者是在可以達到 120 Hz 的設備上提供 120 fps 的性能。
對于 60fps 來說,需要在約每 16ms 的時候渲染一幀。
當 UI 渲染不流暢的時候,卡頓就隨之產生了。舉例來說,如果一幀花了 10 倍的時間來渲染,這幀就會被丟棄,動畫看起來就會卡。
有句話叫“快的應用固然很好,但流暢的應用則更好。”如果你的應用渲染并不流暢,該怎么處理呢?從哪里著手呢?本文展示了應該從哪里著手,步驟以及可以提供幫助的工具。
?備忘
應用的性能不只是由一次測量(measure)決定的。性能有時取決于原生速度,同時也取決于 UI 的流暢性,不卡頓。其他性能指標還包括 I/O 或者網速。本文主要聚焦于第二種性能(UI流暢性),但其中的大多數工具也能被用來分析其他性能問題。
分析 Dart 代碼中的性能問題,可以參考?調試 Flutter 應用?頁下的?跟蹤 Dart 代碼性能。
1. 分析性能問題
分析應用的性能問題需要打開性能監控圖層(performance overlay)來觀察 UI 和 GPU 線程。在此之前,要確保是在分析模式(profile mode)下運行,而且當前設備不是虛擬機。使用用戶可能采用的最慢設備來獲取最佳結果。
1.1 連接到物理設備
幾乎全部的 Flutter 應用性能調試都應該在真實的 Android 或者 iOS 設備上以分析模式進行。通常來說,調試模式或者是模擬器上運行的應用的性能指標和發布模式的表現并不相同。?應該考慮在用戶使用的最慢的設備上檢查性能。
為什么應該在真機上運行:
各種模擬器使用的硬件并不相同,因此性能也不同—模擬器上的一些操作會比真機快,而另一些操作則會比真機慢。
調試模式相比分析模式或者發布編譯來說,增加了額外的檢查(例如斷言),這些檢查可能相當耗費資源。
調試模式和發布模式代碼執行的方式也是不同的。調試編譯采用的是“just in time”(JIT)模式運行應用,而分析和發布模式則是預編譯到本地指令(“ahead of time”,或者叫 AOT)之后再加載到設備中。JIT本身的編譯就可能導致應用暫停,從而導致卡頓。
1.2 在分析模式運行
除了一些調試性能問題所必須的額外方法,Flutter 的分析模式和發布模式的編譯和運行基本相同。例如,分析模式為分析工具提供了追蹤信息。
使用分析模式運行應用的方法:
在 Android Studio 和 IntelliJ 使用?Run > Flutter Run main.dart in Profile Mode?選項
在 VS Code中,打開?launch.json?文件,設置?flutterMode?屬性為?profile(當分析完成后,改回?release?或者?debug):
From the command line, use the?--profile?flag: 命令行使用?--profile?參數運行
關于不同模式的更多信息,請參考?Flutter 的構建模式選擇。
下面我們會從開啟性能圖層開始講述
2. 性能圖層
性能圖層用兩張圖表顯示應用的耗時信息。如果 UI 產生了卡頓(跳幀),這些圖表可以幫助分析原因。圖表在當前應用的最上層展示,但并不是用普通的 widget 方式繪制的—Flutter 引擎自身繪制了該圖層來盡可能減少對性能的影響。每一張圖表都代表當前線程的最近 300 幀表現。
本節闡述如何打開性能圖層并用其來分析應用中卡頓的原因。下面的截圖展示了 Flutter Gallery 樣例的性能圖層:
GPU 線程的性能情況在上面,UI 線程顯示在下面,垂直的綠色條條代表的是當前幀。
Flutter 用了一些額外的線程來完成這項工作。開發者的 Dart 代碼都在 UI 線程運行。盡管沒有直接訪問其他線程的權限,但 UI 線程的動作還是對其他線程的性能有影響的。
(1)平臺線程
該平臺的主線程。插件代碼在這里運行。更多信息請參閱:iOS 的?UIKit?文檔,或者 Android 的?MainThread?文檔。性能圖層并不會展示該線程。
(2)UI 線程
UI 線程在 Dart VM 執行 Dart 代碼。該線程包括開發者寫下的代碼和 Flutter 框架根據應用行為生成的代碼。當應用創建和展示場景的時候,UI 線程首先建立一個?圖層樹(layer tree)?,一個包含設備無關的渲染命令的輕量對象,并將圖層樹發送到 GPU 線程來渲染到設備上。?不要阻塞這個線程!?在性能圖層的最低欄展示該線程。
(3)GPU 線程
GPU 線程取回圖層樹并通知 GPU 渲染。盡管無法直接與 GPU 線程或其數據通信,但如果該線程變慢,一定是開發者 Dart 代碼中的某處導致的。圖形庫 Skia 在該線程運行,有時也被叫做?光柵器(rasterizer)線程?。在性能圖層的最頂欄展示該線程。
(4)I/O 線程
可能阻塞 UI 或者 GPU 線程的耗時任務(大多數情況下是I/O)。該線程并不會在性能圖層中展示。
每一幀都應該在 1/60 秒(大約 16ms)內創建并顯示。如果有一幀超時(任意圖像)而無法顯示,就導致了卡頓,圖表之一就會展示出來一個紅色豎條。如果是在 UI 圖表出現了紅色豎條,則表明 Dart 代碼消耗了大量資源。而如果紅色豎條是在 GPU 圖表出現的,意味著場景太復雜導致無法快速渲染。
紅色豎條表明當前幀的渲染和繪制都很耗時
當兩張圖表都是紅色時,就要開始對 UI 線程(Dart VM)進行診斷了。
2.1 顯示性能圖層
你可以用如下方法顯示性能圖層:
使用 Flutter Inspector
從命令行啟動
寫入代碼
2.1.1 使用 Flutter inspector
打開 PerformanceOverlay widget 最簡單的方法是 IDE 中 Flutter 插件提供的 Flutter inspector。運行應用時會默認打開 Inspector 的窗口。如果沒有打開,可以用下面的方法打開。
Android Studio 和 IntelliJ IDEA:
(1)選擇?View > Tool Windows > Flutter Inspector。
(2)在工具欄中選擇書架圖標 ()。
Flutter Inspector 在 Android Studio 和 IntelliJ 中都可以使用。了解更多可以使用 Inspector 做的事情,可以參閱?Widget inspector?文檔,以及 DartConf 2018 的?Flutter Inspector talk。
2.1.2?VS Code
(1)選擇?View > Command Palette…?來打開 command palette。
(2)在文本框中輸入“performance”并在彈出列表中選中?Toggle Performance Overlay。如果命令不可用,請確保應用在運行狀態。
2.1.3 命令行
使用?p?參數觸發性能圖層。
2.1.4 代碼控制
可以通過在 MaterialApp 或者 WidgetsApp 的構造方法中設置?showPerformanceOverlay?屬性為?true?來展示 PerformanceOverlay widget:
class?MyApp?extends?StatelessWidget?{@overrideWidget?build(BuildContext?context)?{return?MaterialApp(showPerformanceOverlay:?true,title:?'My?Awesome?App',theme:?ThemeData(primarySwatch:?Colors.blue,),home:?MyHomePage(title:?'My?Awesome?App'),);} }可能讀者已經對 Flutter Gallery 樣例應用相當熟悉了。要在 Flutter Gallery 中使用性能圖層,請使用與 Flutter 一起安裝的?examples?目錄的副本在分析模式下運行應用。應用的代碼中已經寫好了通過應用菜單動態觸發圖層,同時允許對?saveLayer?的調用和當前已緩存的圖片的檢查。
?備忘
從應用市場下載的 Flutter Gallery 應用是無法打開性能圖層的。因為該版本是用發布模式編譯的(而不是分析模式),并且沒有提供圖層開關的菜單。
2.2 定位 UI 圖表中的問題
如果性能圖層的 UI 圖表顯示紅色,就要從分析 Dart VM 開始著手了,即使 GPU 圖表同樣顯示紅色。
2.2.1 使用 Dart DevTool 進行性能分析
Dart DevTool 提供諸如性能分析、堆測試以及顯示代碼覆蓋率等功能。DevTool 的?timeline?界面可以讓開發者逐幀分析應用的 UI 性能。
?備忘
Observatory 被 Dart DevTools 取代了。這個基于瀏覽器的工具仍在開發中,但只用來預覽。參考?DevTools’ docs?頁面來獲取安裝和使用指導。
2.3 定位 GPU 圖表中的問題
有些情況下界面的圖層樹構造起來雖然容易,但在 GPU 線程下渲染卻很耗時。這種情況發生時,UI 圖表沒有紅色,但 GPU 圖表會顯示紅色。這時需要找出代碼中導致渲染緩慢的原因。特定類型的負載對 GPU 來說會更加復雜。可能包括不必要的對?saveLayer?的調用,許多對象間的復雜操作,還可能是特定情形下的裁剪或者陰影。
如果推斷的原因是動畫中的卡頓的話,可以使用?timeDilation?屬性來極大地放慢動畫。
也可以使用 Flutter Inspector 來減慢動畫速度。在 inspector 的 gear 菜單下選中?Enable Slow Animations。如果想對動畫速度進行更多操作,請在代碼中設置?timeDilation?屬性。
卡頓是第一幀發生的還是貫穿整個動畫過程呢?如果是整個動畫過程的話,會是裁剪導致的么?也許有可以替代裁剪的方法來繪制場景。比如說,不透明圖層的長方形中用尖角來取代圓角裁剪。如果是一個靜態場景的淡入、旋轉或者其他操作,可以嘗試使用?RepaintBoundary
2.3.1 檢查屏幕之外的視圖
saveLayer?方法是 Flutter 框架中最重量的操作之一。更新屏幕時這個方法很有用,但它可能使應用變慢,如果不是必須的話,應該避免使用這個方法。即便沒有顯式地調用?saveLayer,也可能在其他操作中間接調用了該方法。可以使用?PerformanceOverlayLayer.checkerboardOffscreenLayers?開關來檢查場景是否使用了?saveLayer。
打開開關之后,運行應用并檢查是否有圖像的輪廓閃爍。如果有新的幀渲染的話,容器就會閃爍。舉個例子,也許有一組對象的透明度要使用?saveLayer?來渲染。在這種情況下,相比通過 widget 樹中高層次的父 widget 操作,單獨對每個 widget 來應用透明度可能性能會更好。其他可能大量消耗資源的操作也同理,比如裁剪或者陰影。
注意:透明度、裁剪以及陰影它們本身并不是個糟糕的注意。然而對 widget 樹頂層 widget 的操作可能導致額外對?saveLayer?的調用以及無用的處理。
當遇到對?saveLayer?的調用時,先問問自己:
應用是否需要這個效果?
可以減少調用么?
可以對單獨元素操作而不是一組元素么?
2.3.2 檢查沒有緩存的圖像
使用?RepaintBoundary?來緩存圖片是個好主意,?當需要的時候?。
從資源的角度看,最重量級的操作之一是用圖像文件來渲染紋理。首先,需要從持久存儲中取出壓縮圖像,然后解壓縮到宿主存儲中(GPU 存儲),再傳輸到設備存儲器中(RAM)。
也就是說,圖像的 I/O 操作是重量級的。緩存提供了復雜層次的快照,這樣就可以方便地渲染到隨后的幀中。?因為光柵緩存入口的構建需要大量資源,同時增加了 GPU 存儲的負載,所以只在必須時才緩存圖片。
打開?PerformanceOverlayLayer.checkerboardRasterCacheImages?開關可以檢查哪些圖片被緩存了。
運行應用來查看使用隨機顏色網格渲染的圖像,標識被緩存的圖像。當和場景交互時,網格里的圖片應該是靜止的—代表重新緩存圖片的閃爍視圖不應該出現。
大多數情況下,開發者都希望在網格里看到的是靜態圖片,而不是非靜態圖片。如果靜態圖片沒有被緩存,可以將其放到?RepaintBoundary?widget 中來緩存。雖然引擎也可能忽略 repaint boundary,如果它認為圖像還不夠復雜的話。
2.4 檢視 widget 重建性能
Flutter 框架的設計使得構建達不到 60fps 流暢度的應用變得困難。通常情況下如果卡頓,就是因為每一幀被重建的 UI 比需求更多的簡單 bug。Widget rebuild profiler 可以幫助調試和修復這些問題引起的 bug。
可以檢視 widget inspector 中當前屏幕和幀下的 widget 重建數量。了解細節,可以參考?在 Android Studio 或類 IntelliJ 里開發 Flutter 應用?中的?顯示性能數據。
3. 調試參數
Flutter 提供了大量的調試參數和功能來幫助開發者在開發環節的各個性能點調試應用。這些特性必須在調試模式下編譯。下面未完成的列表高亮了一些?rendering library?中在調試性能問題時最有用的參數(和一個方法)。
可以在編輯框架代碼的時候設置這些參數,或者通過導入 module 并在?main()?方法中設置,然后通過熱重啟應用。
debugDumpRenderTree()
在 widget inspector 中檢視渲染樹,而不是使用這個參數轉儲渲染樹到文件。使用 widget inspector 并選擇?Render Tree?標簽。debugPaintLayerBordersEnabled
debugRepaintRainbowEnabled
可以在 widget inspector 打開?More Actions?菜單,并選擇?Show Repaint Rainbow?來開啟這個參數。如果任何靜態 widget 在循環七彩跑馬燈,(比如說一個靜態的頭部控件),這些區域就有著額外的重繪邊界。debugPrintMarkNeedsLayoutStack
如果發現比預期更多的 layout,打開這個參數,(比如說時間軸上、分析文件中、或者是在一個 layout 方法的?print?狀態內)。開啟后,控制臺會大量輸出堆棧跟蹤信息來展示為什么每個渲染對象被 layout 標記為 dirty。debugPrintMarkNeedsPaintStacks
和?debugPrintMarkNeedsLayoutStack相似,作用于過度繪制。
可以在?調試 Flutter 應用?中了解到其他的調試參數。
4. 評分
可以通過編寫評分測試來測量和追蹤應用的性能。Flutter Driver 庫提供了對評分的支持。基于這套測試框架就可以生成以下幾項的測試標準:
卡頓
下載大小
電池性能
啟動時間
追蹤這些評分可以在回歸測試中了解對性能的不利影響。
了解更多,請參考?測試 Flutter 應用?中的?集成測試?一節。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Flutter 性能分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 特斯拉 CEO 马斯克突访美国会并面见众
- 下一篇: 美股周四:三大股指全线上涨,特斯拉涨近