[譯] Performance testing of Flutter apps

原文連接:Performance testing of Flutter apps 做者:Filip Hracek (from Flutter team)android

Flutter 在默認狀態下就能運行得很是快,這點很是棒! 可是否這就意味着你徹底不須要考慮性能呢?git

答案是否認的,編寫一個速度很是慢的 Flutter 應用是徹底可能的。然而,另外一方面你也能夠充分利用這個框架,讓你的 app 不只快速,高效,並且使用更少的 CPU 時間和電量。github

這就是咱們想要的!在一個有意義的指標上來比較兩個版本的應用在統計上明顯的差別web

在 Flutter 中有一些性能優化的通用指導原則:chrome

  • 更新狀態時,影響範圍儘量地少。
  • 僅當必要時才更新狀態。
  • 不要在 build 方法中進行密集型計算任務,理想的話,在 main isolate 以外執行這些操做。

你可能很難相信,對於大多數性能優化的問題來講,它們的答案通通指向了這句話——「它究竟取決於什麼?」。對於特定 Widget 是否值得進行特定優化,並付出維護成本?在特定狀況下的特殊處理是否合理?shell

對於這些問題惟一有用答案是測試和測量。量化每一個選擇對性能的影響,並根據該數據作出決定。瀏覽器

好消息是 Flutter 提供了出色的性能分析工具,例如包含 Flutter Inspector 的 Dart DevTools(目前還處於預覽版),或是 Android Studio 中的 Flutter Inspector(安裝了 Flutter 插件下)。你可使用 Flutter Driver 操做應用,並在 Profile 模式下保存性能信息。性能優化

然而壞消息是,如今的手機實在是太過智能了。😂bash

管理器的問題(Governors)

系統級守護進程須要根據當前負載調整 CPU 和 GPU 單元的速度,可是 iOS 和 Android 的管理器卻很難量化 Flutter 應用的性能。總的來講這仍是一件好事,由於它確保了平穩的性能,同時也消耗盡量少的電量。網絡

然而缺點是你徹底能夠經過提升其效率以顯著加快應用的運行速度。

下面的例子中,你能夠看到如何在應用中循環打印一些無心義的 print 語句,使得管理器切換到更高的檔位,從而讓應用運行更快速,性能更加可預測。

管理器的問題:在默認狀況下,你沒法相信這些數字。在上面這個盒子圖中,咱們在 x 軸上進行單獨運行(用它們開始的確切時間標記),並在 y 軸上表示構建時間。正如你所看到那樣,當咱們引入一些徹底沒必要要的打印語句時,它居然會縮短(而不是增長) build 時間。

在這個實驗中(上圖),更差的代碼反而致使了更快的構建時間,更快的光柵化時間和更高的幀率。若是客觀上更差的代碼會獲得更好的性能,那麼你就不能遵循這些指標。

上面僅僅是爲了解釋爲什麼移動應用性能基準不直觀,以及測試困難的一個例子。

接下來,我將分享一些 Google I/O app 上 Flutter 的例子,Developer Quest

基本建議

  • 不要在 DEBUG 模式下測量性能。只有 profile 模式下才能測量其性能。
  • 在真機上測試,不要在 Android 或者 iOS 模擬器上測試。雖然模擬器軟件很是適合開發使用,可是它們在性能表現上和真機差別很是大。Flutter 不容許在模擬器上以 profile 模式運行,由於這並無任何意義。這種方式收集的性能數據並非實際的性能。
  • 理想的狀況下是使用相同物理設備。讓它做爲你專用的性能測試機,並再也不用於其餘用途。
  • 學習 Flutter 性能分析工具

CPU / GPU 管理器

正如咱們剛纔所說的,現代操做系統根據負載和一些其餘的啓發式調整每一個 CPU 和 GPU 的頻率。(例如,觸摸屏幕一般會讓 Android 手機將其優先級置於更高的檔位。)

在 Android 上你可以直接關掉這些管理器,咱們稱之爲「scale locking」。

  • 編寫一個腳原本 scale-lock 你的測試機性能。你能夠在 Skia’s recipe 中找到靈感,也能夠查閱 Unix CPU API
  • 除非你正在運行像 Skia 這樣的大型基準測試,一般狀況下你可能想要一些更加輕量,不那麼通用的東西。來看看 Developer Quest 中針對某些方面的 shell 腳本吧。 例如,下面這個將 CPU 管理器設爲 userspace (惟一一個不會自動調整 CPU 頻率的管理器)。
#!/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}"
複製代碼
  • 如今的目標並非要模擬真實性能(沒有用戶 scale-lock 的設備),而是獲取運行時可比較的性能指標。
  • 最後你須要進行測試,並在設備上運行 shell 腳本。只有這樣纔是有效的,在這以前的性能數據都在欺騙你。

使用 Flutter Driver 個人桌面上運行 Developer Quest 的早期版本

Flutter Driver

Flutter Driver 可以自動執行應用。你能夠閱讀 flutter.dev 上性能分析的文章,瞭解如何使用它來分析應用。

  • 不要在性能測試時手動操做你的應用。始終使用 Flutter Driver 確保你的比較具備意義。
  • 編寫你的 Flutter Driver 代碼,以便可以讓其執行你真正想要測試的內容。若是你正在進行常規程序性能測試,請嘗試遍歷所有應用,並執行用戶會作的操做。
  • 若是你的應用具備偶然性(隨機網絡事件等),請使用 mock 數據,而且保證它們儘量類似。
  • 若是須要的話,還可使用 Timeline 的 startSync() 方法 和 finishSync() 方法,來添加自定義時間軸事件。例如,當你想要測試特定方法的性能時,這頗有用。將 startSync() 放在它的開頭,並在方法結束時使用 finishSync()
  • 對於各個不一樣版本的應用,須要進行屢次測試。在 Developer Quest 時我彙總了 100 次。當你測量一些好比像「第 99 位百分數」這樣繁雜的東西時,你得運行更屢次才行。而對於基於 POSIX 的系統來講,只須要運行下面這樣:
for i in {1..100}; do flutter drive --target=test_driver/perf.dart --profile; done
複製代碼

時間軸(Timeline)

時間軸是你運行 profile 模式下輸出的原始資料。Flutter 將此信息轉儲到可被 chrome://tracing 加載的 JSON 文件中。

  • 瞭解如何在 Chrome 的 tracing timeline 中開啓完整的 timeline。你只須要打開 Chrome 瀏覽器的 chrome://tracing,而後點擊「Load」,選擇那個 JSON 文件。你能夠在這篇 簡短指導 中得到關於它的更多信息。(一樣有 Flutter Timeline tooling,但目前還處於 tech preview 階段。由於在 Flutter 的 timeline tooling 準備就緒以前,Developer Quest 項目就已經開始了,因此我還沒用過那個。)
  • 使用 WSAD 鍵在 chrome://tracing 的 timeline 中移動,以及使用 1234 來更改操做模式。
  • 首次設置性能測試時,考慮是否使用 Flutter Driver 運行完整的 Android systrace。這使您能夠更深刻地瞭解設備中實際發生的狀況,包括 CPU 縮放信息。可是,不要使用徹底開啓的 systrace 測量您的應用程序,由於它會讓一切變得很是慢並且更加不可預測。
  • 那麼,應該如何使用 Flutter Driver 運行完整的 Android systrace 呢?首先,你得從 /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 systrace。 而後經過 flutter run test_driver/perf.dart --profile --trace-systrace 運行 app。最後,經過 flutter drive --driver=test_driver/perf_test.dart --use-existing-app=http://127.0.0.1:NNNNN/ 啓動 Flutter Driver(其中 NNNNN 是 Flutter run 上運行的端口給你的)。

度量

最好能看盡量多的指標,但我發現一些相比其餘更有用的指標。

  • build 的時間光柵化的時間 僅在真正嚴格的性能測試中才有用(默認狀況下提供的度量標準是 TimelineSummary),這些測試除了 UI 以外不會包含太多其餘內容。

  • 不要把 TimelineSummary.frameCount 看做計算每秒幀數(FPS)的方法。Flutter 的配置文件工具不能給你提供真實的幀速率信息。TimelineSummary 雖然提供了 countFrames() 方法,但它只會計算已完成的幀構建了多少次。一個優化良好的應用能夠限制沒必要要的重建,每秒的幀數比常常重建的未經優化的應用程序少不少。

  • 我我的得到的最有用的數據是經過測量運行 Dart 代碼所花費的總 CPU 時間獲得的。這會算上 build 方法和外部執行的代碼。假設你在 scale-locked 的設備上運行配置文件測試,總 CPU 時間能夠很好地估算你的應用程序將消耗更多仍是更少的電。

  • 要找出 Dart 代碼運行所花費的總 CPU 時間,最簡單的方法是看 timeline 中 MessageLoop:FlushTasks 事件的範圍。在製做 Developer Quest 的時候,我編寫了一個 Dart 工具來提取它們。
  • 要檢測 jank (即跳過的幀),請尋找極端狀況。例如,對於 Developer Quest 的特定狀況以及咱們用於測試的設備來講,觀察 95% 的 build 時間頗有幫助。(90% 的 build 時間都很類似,即便是在比較效率水平差距很是大的代碼也是同樣,而 99% 每每又過於亂了。但你的狀況可能會有所不一樣。)

  • 正如我以前所提到的那樣,對於你的每一個版本的應用都演算它上百次, 而後使用有些許偏差的平均值或百分位數據。若是使用箱形圖就更好了!

結論

當這些都設置好了以後,你就可以自信地比較提交和實驗。下面,你能夠看到一個很常見困境的答案:「這種優化值得維護開銷嗎?」

我認爲對於這個狀況來講,答案是確定的。只需幾行代碼,咱們應用的每一個自動測試平都可以減小 12% 的 CPU 時間。

然而,本文的主要內容是,不一樣的測量方式可能會反映處很是不一樣的東西。嘗試過於寬泛地推斷性能測量也許十分符合直覺,但倒是錯誤的。

換句話說:「它究竟取決於什麼」。咱們應該信奉這句話。

以上即是翻譯的所有內容,我認爲這篇文章對咱們構建高效的 Flutter 應用具備很大的指導意義,因此便翻譯出來分享給你們。

如如有譯誤還請指出。

相關文章
相關標籤/搜索