0%

【文章翻譯】Performance Testing of Flutter apps

【文章內容使用 Gemini 1.5 Pro 自動產生】

Flutter 應用程式的效能測試

Flutter 框架在預設情況下非常快速,但這是否意味著您不必考慮效能呢?不。絕對有可能撰寫出速度緩慢的 Flutter 應用程式。另一方面,也有可能充分利用框架,讓您的應用程式不僅速度快,而且高效,消耗更少的 CPU 時間和電池。

這就是我們想要的:一個在某些有意義的指標上比較您的應用程式的兩個版本時具有統計顯著性的結果。請繼續閱讀以了解如何獲得此結果。

Flutter 效能優化的概略準則如下:

  • 更新狀態時,目標盡可能少的 Widget。
  • 僅在必須時更新狀態。
  • 將計算密集型任務放在建構方法之外,理想情況下是在主隔離區之外。

事實上,對於許多有關效能优化的問題,答案是「視情況而定」。對 特定 Widget 進行的 特定 優化是否值得付出努力和維護成本?特定 情況下的 特定 方法是否有意義?

這些問題唯一有用的答案來自測試和測量。量化每個選擇對效能的影響,並根據這些資料做出決定。

好消息是,Flutter 提供了出色的效能分析工具,例如 Dart DevTools(目前處於預覽發行階段),其中包含 Flutter Inspector,或者您可以直接從 Android Studio(安裝 Flutter 外掛後)使用 Flutter Inspector。您有 Flutter Driver 用於測試您的應用程式,以及用於儲存效能資訊的 Profile 模式。

壞消息是現代智慧型手機非常「聰明」。

治理器的問題

iOS 和 Android 治理器使量化 Flutter 應用程式的效能變得特別困難。這些系統級別的守護程序會根據負載調整 CPU 和 GPU 單位的速度。當然,這大多是好事,因為它確保在消耗盡可能少的電池的情況下提供流暢的效能。

缺點是,您可以透過讓應用程式 更多 地工作來使其顯著 更快

在下方,您可以看到如何將具有無意義 print 語句的迴圈加入到應用程式中,使治理器切換到更高檔,從而使應用程式更快,其效能也更可預測。

治理器的問題:預設情況是,您無法信任您的數字。在這個箱型圖中,我們在 x 軸上有單獨的執行(以它們開始的精確時間標記),以及 y 軸上的建構時間。如您所見,當我們加入一些完全不必要的 `print` 語句時,它會使建構時間下降,而不是上升。

在此實驗中,較差的程式碼導致更快的建構時間(上方)、更快的柵格化時間和更高的畫面更新率。當客觀上較差的程式碼導致更好的效能指標時,您便無法依賴這些指標來提供指導。

這只是一個例子,說明行動應用程式的效能基準測試為何會不合常理且困難。

在下方,我分享了一些我在處理 Flutter 的 Google I/O 應用程式 Developer Quest 時收集的技巧。

基本建議

  • 在 DEBUG 模式下不要測量效能。僅在 profile 模式下測量效能。
  • 在真實設備上測量,而不是在 iOS 模擬器或 Android 模擬器中。軟體模擬器非常適合開發,但它們與真實設備的效能特性有很大差異。Flutter 不允許您在模擬設備上以 profile 模式執行,因為這沒有任何意義。您以這種方式收集的資料不適用於現實世界的效能。
  • 理想情況下,使用相同的物理設備。將其作為您的專用效能測試設備,不要用於其他任何用途。
  • 了解 Flutter 的 效能分析工具

CPU/GPU 治理器

如上所述,現代作業系統會根據負載和其他一些啟發式算法更改其處置的每個 CPU 和 GPU 的頻率。(例如,觸碰螢幕通常會使 Android 手機切換到更高檔。)

在 Android 上,您可以關閉這些治理器。我們稱此過程為「比例鎖定」。

  • 建立一個比例鎖定效能測試設備的腳本。您可以使用 Skia 的做法 作為靈感。您也可以查看 Unix CPU API
  • 除非您執行的基準測試作業規模龐大,例如 Skia,否則您可能想要更輕量級且不那麼通用的工具。查看 Developer Quest 的 shell 腳本 以獲取一些提示。例如,以下摘錄將 CPU 設為使用者空間治理器(唯一不會自行更改 CPU 頻率的治理器)。
1
2
3
4
5
6
7
#!/usr/bin/env bash

GOV="userspace"
echo "Setting CPU governor to: ${GOV}"
adb shell "echo ${GOV} > /sys/devices/system/cpu/cpu${CPU_NO}/cpufreq/scaling_governor"
ACTUAL_GOV=`adb shell "cat /sys/devices/system/cpu/cpu${CPU_NO}/cpufreq/scaling_governor"`
echo "- result: ${ACTUAL_GOV}"
  • 您的目標在此不是模擬現實世界的效能(沒有使用者會比例鎖定其設備),而是讓不同執行之間的效能指標具有可比性。
  • 最終,您需要進行實驗,並將 shell 腳本調整為您將使用的設備。這項工作很繁瑣,但直到您完成這項工作之前,您的效能資料都會欺騙您。
Developer Quest 的早期版本在桌面上由 Flutter Driver 測試。

Flutter Driver

Flutter Driver 讓您可以自動測試您的應用程式。請閱讀 flutter.dev 的 效能分析 部分,以獲取有關如何在分析應用程式時使用它的具體做法。

  • 在效能測試時,不要手動測試您的應用 Programm。始終使用 Flutter Driver 以確保您比較的是同樣的事物。
  • 撰寫 Flutter Driver 程式碼,讓它測試您真正想要測量的內容。如果您追求的是一般的應用程式效能,請嘗試瀏覽應用程式的各個部分,並執行使用者會執行的操作。
  • 如果您的應用程式包含機率因素(隨機、網路事件等),請將其模擬出來。這些瀏覽過程應盡可能彼此相似。
  • 如果需要,請使用 TimelinestartSync()finishSync() 方法來新增自訂時間軸事件。例如,當您感興趣的是特定函數的效能時,此方法非常有用。將 startSync() 放在其開頭,將 finishSync() 放在其結束位置。
  • 儲存摘要(writeSummaryToFile)和更重要的是原始時間軸(writeTimelineToFile)。
  • 對於應用程式的每個版本,請執行多次測試。對於 Developer Quest,我將其收斂到 100 次執行。(當您測量雜訊較大的事物時,例如第 99 個百分位數,您可能需要更多次執行。)對於基於 POSIX 的系統,這只意味著執行類似以下內容:for i in {1..100}; do flutter drive --target=test_driver/perf.dart --profile; done
使用 Chrome 的時間軸工具檢查 Flutter 的 profile 輸出。

時間軸

時間軸是 profile 執行結果的原始輸出。Flutter 將此資訊轉儲到一個 JSON 檔案中,可以將其載入到 chrome://tracing 中。

  • 了解如何在 Chrome 的追蹤時間軸中打開完整時間軸。您只需要在 Chrome 瀏覽器中打開 chrome://tracing,點選「載入」,然後選擇 JSON 檔案。您可以在 這個簡短的教學課程 中了解更多資訊。(Flutter 也提供了 時間軸工具,目前處於技術預覽階段。我沒有使用它,因為 Developer Quest 專案在 Flutter 的時間軸工具準備就緒之前就已經開始了。)
  • 使用 WSAD 鍵在 chrome://tracing 中移動時間軸,並使用 1234 切換操作模式。
  • 首次設定效能測試時,請考慮使用完整的 Android 系統追蹤來執行 Flutter Driver。這為您提供了更多關於設備中實際發生的事情的洞察力,包括 CPU 比例資訊。但是,請不要在完全開啟系統追蹤的情況下測量您的應用程式,因為它會使一切都變得更慢且更不可預測。
  • 如何使用 Flutter Driver 執行完整的 Android 系統追蹤?首先,使用 /path/to/your/android/sdk/platform-tools/systrace/systrace.py --atrace-categories=gfx,input,view,webview,wm,am,sm,audio,video,camera,hal,app,res,dalvik,rs,bionic,power,pm,ss,database,network,adb,pdx,sched,irq,freq,idle,disk,load,workq,memreclaim,regulators,binder_driver,binder_lock 開始 Android 系統追蹤。然後,使用 flutter run test_driver/perf.dart --profile --trace-systrace 啟動應用程式。最後,使用 flutter drive --driver=test_driver/perf_test.dart --use-existing-app=http://127.0.0.1:NNNNN/(其中 NNNNN 是 flutter run 上面的埠號)啟動 Flutter Driver。

指標

查看盡可能多的指標總比少好,但我發現有些指標比其他指標更有用。

  • 建構時間和柵格化時間(TimelineSummary 預設提供的指標)僅適用於實際上不包含太多 UI 建構以外的內容的非常嚴格的效能測試。
  • 不要將 TimelineSummary.frameCount 作為計算畫面更新率 (FPS) 的方法。Flutter 的 profile 工具不會提供實際的畫面更新率資訊。TimelineSummary 提供 countFrames() 方法,但它只計算已完成的畫面建構次數。一個經過良好優化的應用程式會限制不必要的重新建構,其畫面更新率將低於頻繁重新建構的未優化應用程式。
  • 我個人透過測量執行 Dart 程式碼所花費的總 CPU 時間來獲得最有用的資料。這計算了在建構方法中以及在建構方法之外執行的程式碼。假設您在比例鎖定的設備上執行 profile 測試,則總 CPU 時間是應用程式將消耗多少電池電量的良好近似值。
  • 找出執行 Dart 程式碼所花費的總 CPU 時間最簡單的方法是測量時間軸中 MessageLoop:FlushTasks 事件的範圍。對於 Developer Quest,我撰寫了一個 Dart 工具 來提取這些事件
  • 若要偵測卡頓(即跳過的畫面),請尋找極端值。例如,對於 Developer Quest 的特定案例和我們用於測試的設備,查看第 95 個百分位數的建構時間很有幫助。(即使比較具有截然不同的效率水準的程式碼,第 90 個百分位數的建構時間也過於相似,而第 99 個百分位數的數字往往會出現雜訊。您的情況可能有所不同。)
  • 如上所述,請對應用程式的每個版本執行多次測試(可能 100 次)。然後,使用具有誤差範圍的平均值或百分位數資料。更好的是,使用箱型圖。

結果

設定完畢後,您便能夠自信地比較提交和實驗。在下方,您可以看到針對常見困境的答案:「這個優化是否值得維護開銷?」

我認為在 特定 案例中,答案是肯定的。只需增加幾行程式碼,我們應用程式的每次自動瀏覽平均可以節省 12% 的 CPU 時間。

但是,這是本文的主要訊息 - 另一項優化的測量結果可能會顯示出截然不同的情況。試圖過度外推效能測量結果是引人入勝的,但錯誤的做法。

換句話說:「視情況而定」。我們應該接受這句話。


Flutter 應用程式的效能測試 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

http://creativecommons.org/licenses/by/4.0/