camera客观测试_Flutter 应用性能测试

a72dc8408fb2f77b0171f765cc62699f.png

作者 | Filip Hracek 
翻译 | Vadaski ,来自小集团队
来源 | Performance testing of Flutter apps1

Flutter 在默认状态下就能运行得非常快,这点非常棒! 但是否这就意味着你完全不需要考虑性能呢?

答案是否定的,编写一个速度非常慢的 Flutter 应用是完全可能的。然而,另一方面你也可以充分利用这个框架,让你的 app 不仅快速,高效,而且使用更少的 CPU 时间和电量。

82935cb5b5ded430c8e73a20649f2daa.png

这就是我们想要的!在一个有意义的指标上来比较两个版本的应用在统计上明显的差异

在 Flutter 中有一些性能优化的通用指导原则:

• 更新状态时,影响范围尽可能地少。

• 仅当必要时才更新状态。

• 不要在 build 方法中进行密集型计算任务,理想的话,在 main isolate 之外执行这些操作。

你可能很难相信,对于大多数性能优化的问题来说,它们的答案统统指向了这句话——“它究竟取决于什么?”。对于特定 Widget 是否值得进行特定优化,并付出维护成本?在特定情况下的特殊处理是否合理?

对于这些问题唯一有用答案是测试和测量。量化每个选择对性能的影响,并根据该数据做出决定。

好消息是 Flutter 提供了出色的性能分析工具,例如包含 Flutter Inspector 的 Dart DevTools2(目前还处于预览版),或是 Android Studio 中的 Flutter Inspector3(安装了 Flutter 插件下)。你可以使用 Flutter Driver 操作应用,并在 Profile 模式下保存性能信息。

然而坏消息是,现在的手机实在是太过智能了。?

管理器的问题(Governors)

系统级守护进程需要根据当前负载调整 CPU 和 GPU 单元的速度,但是 iOS 和 Android 的管理器却很难量化 Flutter 应用的性能。总的来说这还是一件好事,因为它确保了平稳的性能,同时也消耗尽可能少的电量。

然而缺点是你完全可以通过提高其效率以显著加快应用的运行速度。

下面的例子中,你可以看到如何在应用中循环打印一些无意义的 print 语句,使得管理器切换到更高的档位,从而让应用运行更快速,性能更加可预测。

1c1607699c6e7cabc85c35e36f1c8e81.png

管理器的问题:在默认情况下,你无法相信这些数字。在上面这个盒子图中,我们在 x 轴上进行单独运行(用它们开始的确切时间标记),并在 y 轴上表示构建时间。正如你所看到那样,当我们引入一些完全不必要的打印语句时,它竟然会缩短(而不是增加) build 时间。

在这个实验中(上图),更差的代码反而导致了更快的构建时间,更快的光栅化时间和更高的帧率。如果客观上更差的代码会得到更好的性能,那么你就不能遵循这些指标。

上面仅仅是为了解释为何移动应用性能基准不直观,以及测试困难的一个例子。

接下来,我将分享一些 Google I/O app 上 Flutter 的例子,Developer Quest4

基本建议

• 不要在 DEBUG 模式下测量性能。只有 profile 模式下才能测量其性能。

• 在真机上测试,不要在 Android 或者 iOS 模拟器上测试。虽然模拟器软件非常适合开发使用,但是它们在性能表现上和真机差异非常大。Flutter 不允许在模拟器上以 profile 模式运行,因为这并没有任何意义。这种方式收集的性能数据并不是实际的性能。

• 理想的情况下是使用相同物理设备。让它作为你专用的性能测试机,并不再用于其他用途。

• 学习 Flutter 性能分析工具

CPU / GPU 管理器

正如我们刚才所说的,现代操作系统根据负载和一些其他的启发式调整每个 CPU 和 GPU 的频率。(例如,触摸屏幕通常会让 Android 手机将其优先级置于更高的档位。)

在 Android 上你能够直接关掉这些管理器,我们称之为“scale locking”。

• 编写一个脚本来 scale-lock 你的测试机性能。你可以在 Skia’s recipe5 中找到灵感,也可以查阅 Unix CPU API6

• 除非你正在运行像 Skia 这样的大型基准测试,通常情况下你可能想要一些更加轻量,不那么通用的东西。来看看 Developer Quest 中针对某些方面的 shell 脚本7吧。例如,下面这个将 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

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

03e3b29e35c5ddf8b8037d89a7108b7b.png

时间轴(Timeline)

时间轴是你运行 profile 模式下输出的原始资料。Flutter 将此信息转储到可被 chrome://tracing 加载的 JSON 文件中。

• 了解如何在 Chrome 的 tracing timeline 中开启完整的 timeline。你只需要打开 Chrome 浏览器的 chrome://tracing,然后点击“Load”,选择那个 JSON 文件。你可以在这篇 简短指导8 中获得关于它的更多信息。(同样有 Flutter Timeline tooling9,但目前还处于 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 时间可以很好地估算你的应用程序将消耗更多还是更少的电。

334e4dfb194862f99892fa7ce1e458d6.png

• 要找出 Dart 代码运行所花费的总 CPU 时间,最简单的方法是看 timeline 中 MessageLoop:FlushTasks 事件的范围。在制作 Developer Quest 的时候,我编写了一个 Dart 工具来提取它们。

• 要检测 jank (即跳过的帧),请寻找极端情况。例如,对于 Developer Quest 的特定情况以及我们用于测试的设备来说,观察 95% 的 build 时间很有帮助。(90% 的 build 时间都很相似,即使是在比较效率水平差距非常大的代码也是一样,而 99% 往往又过于乱了。但你的情况可能会有所不同。)

c1aec6695410dbdaefe74feb8c2c2747.png

• 正如我之前所提到的那样,对于你的每个版本的应用都演算它上百次, 然后使用有些许误差的平均值或百分位数据。如果使用箱形图就更好了!

结论

当这些都设置好了之后,你就能够自信地比较提交和实验。下面,你可以看到一个很常见困境的答案:“这种优化值得维护开销吗?”

ba1fdeb06424466c86eace54f0406ff7.png

我认为对于这个情况来说,答案是肯定的。只需几行代码,我们应用的每个自动测试平均可以减少 12% 的 CPU 时间。

然而,本文的主要内容是,不同的测量方式可能会反映处非常不同的东西。尝试过于宽泛地推断性能测量也许十分符合直觉,但却是错误的。

换句话说:“它究竟取决于什么”。我们应该信奉这句话。

以上便是翻译的全部内容,我认为这篇文章对我们构建高效的 Flutter 应用具有很大的指导意义,所以便翻译出来分享给大家。

如若有译误还请指出。

参考

[1]https://medium.com/flutter/performance-testing-of-flutter-apps-df7669bb7df7 
[2]https://flutter.dev/docs/development/tools/devtools/overview 
[3]https://flutter.dev/docs/development/tools/devtools/inspector 
[4]https://github.com/2d-inc/developer_quest 
[5]https://github.com/google/skia/blob/e25b4472cdd9f09cd393c9c34651218507c9847b/infra/bots/recipe_modules/flavor/android.py 
[6]https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu 
[7]https://github.com/2d-inc/developer_quest/blob/master/tool/lock_android_scaling.sh 
[8]https://aras-p.info/blog/2017/01/23/Chrome-Tracing-as-Profiler-Frontend/ 
[9]https://flutter.dev/docs/development/tools/devtools/timeline

小集 Flutter 小分队

本文由小集团队的小伙伴 Vadaski 翻译。我们的 flutter 小分队在准备了两周后,开始不断产出新的内容,我们把这些内容放到了新建的 repo https://github.com/awesome-tips/Flutter-Tips 中分享给大家,欢迎大家关注 star。

06e405187e055f3cbc2fbc1d4c88d886.png


推荐阅读
• 用 Dart 来写 Objective-C 代码 • Google 是如何做 Code Review 的 • iOS卡顿监测方案总结 • 我开发了一个SwiftUI库,将CSS引入iOS开发
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值