[译] 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 应用具备很大的指导意义,因此便翻译出来分享给你们。

如如有译误还请指出。

相关文章
相关标签/搜索