Flutter 性能检测

官方文档

你将学到什么

  • Flutter的目标是提供60/120FPS
  • 60FPS的设备中每一帧大约需要消耗16ms才能保持无感知的流畅度
  • 当渲染不流畅时,动画会有明显的卡顿现象

一个应用程序的性能取决于多项指标,该篇文章关注的是UI的流畅度。

诊断性能

为了诊断性能问题,需要将性能面板打开以观察UI和raster threads(previously known as the GPU thread)。开始诊断前,要确保flutter应用是在profile mode 并且在真机上运行。

连接真机

为什么要真机调试?

  • Simulator 和 Emulator 使用的硬件不同,所以在性能表现上有些操作比真机快,有些操作比真机慢。
  • Debug模式有一些检查(比如断言),而这些检查不会在profile mode 和 release mode执行,然而这些检查会比较昂贵。
  • 与Release模式比较,Debug模式会已不同的模式执行代码。Debug模式下 Dart代码会以JIT(just in time)模式运行,但是profile和release模式会以AOT(ahead of time)运行。AOT和JIT两者的不同之处在于,AOT是程序执行之前预编译,JIT是程序运行时编译,也就会导致程序性能的下降。

Profile模式下执行

Profile模式与Release模式几乎一致,但会保证最少的附加项来确保调试性能问题。比如Profile模式将追踪数据提供给性能检测的工具。
在Android Studio中可以只用Run > Flutter Run main.dart in Profile Mode去以Profile模式与行。
也可以在终端中使用命令执行

flutter run --profile

打开DevTools

DevTools提供很多功能 (比如profiling,examining the heap, displaying code coverage, enabling the performance overlay,step-by-step debuger)
DevTools的Timeline View可以让你逐帧研究应用程序的UI性能。
当App在profile模式下运行时,点击下面按钮
企业微信20200402042335

The performance overlay(性能监控面板?)

性能监控面板展示为两个图谱展示时间都花在哪里了。如果掉帧这两个图谱会告诉你为什么。这个面板不是一般的Widget,Flutter会以最少的性能影响绘制这个面板。
performance-overlay-green-bb41b466cf6bcd529b285e1510b638086fc5afb8921b8ac5a6565dee5b

性能监控面板展示着raster thread(上)和UI thead(下)。
绿线表示当前帧。

解释图谱

上方的图谱在raster thread消耗的时间,下方的图谱显示UI thread。以16ms/frame为基准如果超过该线,就说明有卡顿现象,需要办法去解决这个问题。图谱只会在应用开始绘制时才会更新。
绘制的每一帧需要在16ms之内完成绘制。如果一帧的绘制超过这个限制,会导致卡顿,这个现象会以垂直的红线展示在两个图谱中。如果红线出现在UI graph,说明此处的Dart代码执行比较耗时,如果红线出现在GPU Graph,说明此处的场景(scene)太复杂,导致渲染比较耗时。performance-overlay-jank-8223103d5cd54de5ba5e590203026d2d658ae5ff16e13aa37b8cae27fd61242d
上图中的两条红线表示当前的帧绘制在渲染(render)和绘制(paint)(这里有我自己的理解 paint 和 render 翻译过来意思差不多,我个人的理解paint是GPU的动作 render是CPU在为GPU提供数据做准备)。
当两个绘制一帧时,两个图谱中都出现红线时,从解决 UI Thread开始。

Flutter的线程

Flutter在多个线程中完成它的工作,尽管它在性能监控面板中只显示两个线程。所有的Dart代码都在UI Thread中执行,尽管您没有直接访问任何其他线程的权限,但是您在UI线程上执行的操作会对其他线程产生性能影响。

Platform thread
平台的主线程。Plugin的代码在这里执行。这个线程没有在性能监控面板中展示。
UI thread
UI thread在Dart虚拟机(VM)中执行Dart代码。这个线程包含了你写的代码和(code executed by Flutter’s framework on your app’s behalf)。当你的app创建并展示scene时,UI thread 会创建layer树,包含与设备无关的绘画命令的轻量对象,然后将layer树发送到raster thread,以便渲染到设备。
Raster thread(previously known as the GPU thread)
raster thread使用layer树与GPU交互并展示在显示器上。你不可以直接访问raster thread和它的数据,但是如果这个线程缓慢,这可能是你在Dart代码中做了什么坏事儿。Skia(图像库)就在这个线程中执行。该线程以前称为“ GPU线程”,因为它为GPU栅格化了。但是它运行在CPU上。我们命名为Raster thread,因为大部分开发者错误地认为该线程运行在GPU上。
I/O thread
执行可能造成在Raster thread和UI Thread耗性能的操作(大部分是I/O)。这个线程不会在性能监控面板中显示。

定义UI Graph的问题

如果性能监控面板的UI graph显示红色,先从 Dart VM开始抓起。

定义GPU Graph的问题

有些时候layer tree很容易被构建,但是Raster thread中却非常耗时。当这种现象发生时,UI graph没有红色,但是GPU graph中显示红色。我们必须找出那些代码导致渲染流程如此耗时。对于GPU,特定种类的工作负载更加困难。它们可能涉及对saveLayer的不必要调用,与多个对象相交的不透明性以及在特定情况下的剪辑或阴影。

如果你认为卡顿的原因是因为它的动画所造成的,你可以在Flutter inspector中点击Slow Animations让动画缓慢地播放。如果你想更灵活地控制动画速度,你也可以使用代码去做。

卡顿是在动画第一帧还是在整个过程中都卡顿?如果因为cliping导致整个过程都在卡顿,你可以尝试使用使用内部透明的视图去盖住这个视图。 If it’s a static scene that’s being faded, rotated, or otherwise manipulated, a RepaintBoundary might help.

检查离屏渲染

saveLayer方法在Flutter中是最耗性能的操作。这个方法在提前处理scene时会比较有用,但是他会让你的app变得缓慢,所以要尽量避免使用这个方法。即使您没有显式调用saveLayer,隐式调用也可能代表您发生。你可以检查是不是因为使用PerformanceOverlayLayer.checkerboardOffscreenLayers开关导致的问题。

启用此开关后,运行应用程序并查找带有闪烁框概述的所有图像。 如果正在渲染新帧,则框会在帧之间闪烁。 例如,也许您有一组具有不透明性的对象,这些对象使用saveLayer渲染。 在这种情况下,将透明性应用于每个单独的小部件,而不是在小部件树中更高一级的父小部件,可能会更有效率。 其他可能昂贵的操作(例如剪切或阴影)也是如此。

Opacity,clipping,shadows本身并不是一个坏主意。 但是,将它们应用于窗口小部件树的顶部可能会导致对saveLayer的额外调用以及不必要的处理。

当你想使用saveLayer时,先问问自己下面这几个问题:

  • app真的需要这个效果吗?
  • 任何这个调用可以被排除吗?
  • 我可以使用单独的元素去替代group吗?

检查未缓存的图片

在合理的情况下,使用RepaintBoundary缓存图像是很好的。从资源的角度来看,最昂贵的操作之一是使用图像文件渲染纹理。首先,从磁盘中获取压缩的图像。图片被解压缩到host memory(GPU memory)中,并传输到device memory(RAM)。换句话说,图片I/O可能很昂贵。由于栅格缓存条目的构建和占用GPU内存的开销很大,因此仅在绝对必要时才缓存图像。您可以通过启用PerformanceOverlayLayer.checkerboardRasterCacheImages开关来查看正在缓存的图像。

运行该应用程序,然后查找带有随机彩色棋盘格的图像,表明该图像已缓存。 与场景互动时,棋盘格图像应保持不变-您不希望看到闪烁,这表示已缓存的图像正在重新缓存。

在大多数情况下,您希望在静态图像上看到棋盘格,但在非静态图像上看不到。 如果未缓存静态图片,则可以将其放入RepaintBoundary小部件中进行缓存。 尽管如果引擎认为图像不够复杂,它可能仍会忽略重绘边界。

查看widget rebuild profiler

Flutter框架设计初衷是使创建60fps且流畅的应用程序变得简单。 通常,如果您有问题,那是因为存在一个简单的错误,导致每帧都需要重建更多的UI。 Widget rebuild profiler可帮助您调试和修复由于此类错误而导致的性能问题。

您可以在Flutter插件(适用于Android Studio)中查看当前屏幕和框架的Widget重建计数。 有关如何执行此操作的详细信息,请参阅显示性能数据

Benchmarking

你可以通过Benchmark测试区最综合测量性能。Flutter Driver library提供benchmarking的功能。通过集成该测试框架,你可以测量以下几个指标:

  • 卡顿
  • 下载数据大小
  • 耗电
  • 启动时长
You Might Also Like
发表评论