使用Timeline可以查找和解决应用程序中的特定性能问题。它也是一个很好的工具,可以识别出Flutter所提供的所有特性的相对性能成本,并允许您做出更明智的决定,确定哪些地方需要避免某些特性,哪些地方需要使用可能会让您的应用程序脱颖而出的效果 .
Flutter 提供的一个开箱即用的性能分析工具去记录Dart Timeline的轨迹。Timeline 工具让您能够询问和回答为什么您的应用程序可能会janking的具体原因。作为经常被指派在不熟悉的代码库中查看性能问题的人员,使用Timeline工具进行概要分析和很轻松,压根不需要你对代码又多了解。因此,Timeline通常是我个人用来诊断和修复性能问题的第一个也是最有用的工具。对于是Flutter本身的性能瓶颈,请直接提交一个问题,其中,你需要提供包必要的测试用例和Timeline trace,来帮助Flutter开发人员更容易识别(交叉引用和优化特定任务),已便进一步提升Flutter引擎和框架的性能。我必须承认,对于同样优先级的问题,我将首先选择带有附加Timeline trace的问题。
时间轴是一个环形缓冲区,记录应用程序代码在其运行过程中记录的事件。要记录的事件类型及其记录频率由发出事件的子系统的作者确定的与性能可能相关的内容决定。.
要使用 Timeline, 请遵循:
记录在循环缓冲区中的事件是非常轻量级的。要以可诊断的形式实际查看这些事件,必须将其导出为适当的可移植格式。Trace Event Format被Flutter用来导出这些时间轴事件,以便在专用的跟踪查看器中查看。这和Catapult开发的性能概要收集、显示和分析家族工具有着相同的格式和查看器.
跟踪事件格式和查看器,并被许多其他项目使用。这些项目包括Chromium和Android(通过systrace)。事实上,它是如此有用,Chromium已经内置。可以尝试在基于chrome的浏览器中导航到chrome://tracing.
您与其他开发人员共享的Traces是JSON文件或其tarball。直到最近我才知道,trace viewer可以自动解压缩JSON文件tarball。.
在我们尝试识别潜在的性能问题之前,我们需要对一个健康(大概是指基本无性能问题)的Flutter应用程序有一些了解。本节是一个关于Flutter如何渲染帧的一个快速介绍.
当Flutter应用启动时,它又启动(或从池中挑选)三个线程,这些线程有时有重叠的区域, 但大体上讲,它们被称为UI线程、GPU线程和IO线程. 这里需要注意的是UI线程和原生如Android平台的UI线程(主线程)并不是一回事,通常Android平台上称UI线程为主线程,然而,在Flutter中我们要注意,你眼里的主线程其实在Flutter这里是Platform thread.
UI线程是所有代表框架执行的Dart代码和应用程序运行的线程。 Unless your application or the framework (via methods like foundation::compute) launches its own isolates (via methods like Isolate.spawn), Dart code will never run on any other thread
一般来说dart代码将永远运行在UI线程,绝对不会跑到其他线程上,除非你的应用程序或框架通过以下两种方式启动专属的isolates:
而且要记住的是:即便启动一个新的 isolate , Dart code 也永远不可能会在 GPU线程 或者 IO 线程上执行。
平台线程是所有依赖插件的代码运行的地方。这个线程也是平台的本地框架为它们的任务提供服务的地方。Flutter应用程序以一种异步的方式与它们的插件进行交互,并且插件不应该去够阻塞任何由Flutter管理的线程.
除了上述四个线程之外,Dart VM还管理一个线程池。这个线程池用于服务多种功能,如等待socket for dart:io、垃圾收集和JIT代码生成(仅在debug模式下,我们知道Flutter在release模式下使用AOT,所以release模式是没有JIT代码生成线程【译者注】)。.
为了产生一个单一的帧,Flutter引擎首先装备一个vsync锁存器。vsync事件指示Flutter引擎开始工作,最终在屏幕上呈现新帧。平台生成的vsync事件考虑了显示的刷新率(以及其他因素),也就是说,平台生成vsync事件也有策略的,掉帧的化,vsync事件的生成应该会受影响【译者注】。
vsync事件唤醒UI线程。还记得吗,UI线程是Dart代码运行的地方。UI线程上的所有操作的结果是一个layer tree,他将交给后端(OpenGL、Vulkan或Software)去程序到屏幕上。
一旦layer tree层树被创建,GPU线程被唤醒并开始转换layer tree到一个GPU命令缓冲区。然后这个命令缓冲区被提交给同一线程上的GPU.
对于足够复杂的场景,UI线程可以并发地生成下一帧,因为GPU线程正在消耗前一帧的layer tree。将一个UI和GPU线程上串且完成看成一个单元的话,这个单元就叫pipeline Item。pipeline深度是引擎在任何给定时间所处理的帧工作负载的数量。管道深度可能不同.
特定的操作模式,掉帧现象可以在一个Flutter应用程序中被感知,比如满足下列条件之一:
这个列表中一个值得注意的例外是,引擎有选择地以一致的速度忽略vsync事件。例如,在60Hz的显示器上,如果引擎仅在其他vsync脉冲上的管道项上开始工作,那么Flutter应用程序将呈现一致的30Hz.
有了对以上掉帧相关的了解,现在我们可以自己搜集Timeline Traces了。
跟踪收集开销相当低,但对性能也有些许影响!因此,Flutter引擎仅在debug或profile模式中收集跟踪。profile模式与用户在运行应用程序时所期望的性能最为相似。此模式使用AOT编译您的Dart代码,与release模式类似。然而,它也支持时间轴,以及通过Dart observatory站提供的其他跟踪工具.
profile模式构建所需的时间与release模式相同,意思是会比debu耗时长点【译者注】。但是,不要因此而放弃使用profile模式。我通常喜欢在debug模式下向时间轴添加跟踪(主要是有HotReload)。然后,当我合理地确信我的跟踪将收集我需要的信息时,我在profile模式中执行一个构建来收集有效的timing information.
要收集 traces,你需要打开 Dart Observatory , Observatory 是一个 web应用 application 服务端,主要你的flutter应用已debug,或profile模式跑起来,都会起一个服务,并且随机开一个端口,Flutter工具转发此端口到您的PC机器上。.
注意: Observatory 将会被 Dart DevTools 取代. 目前是preview版本. 参考资料 DevTools’ docs and, using the timeline view.
Either click on the timeline link on the main observatory page or navigate to the timeline directly by opening http://127.0.0.1:61102/#/timeline
(replace the # with the actual port number)
点击main observatory主页上的时间轴链接,或者打开http://127.0.0.1:61102/#/timeline 直接进入时间轴(用实际端口号替换时间轴)
.
细心的人会发现,如果想指定端口,可以使用, — observatory-port=<port>
. 输入 flutter help run 可以看更多细节.
点击之后,就看到.
通过点击all启用所有跟踪类别,一般勾选Flutter developer,然后点击clear按钮开始跟踪设备。请注意,“all”都已启用但时间轴仍然是空的,但设备已经开始收集痕迹。确保以向时间轴添加跟踪的方式与应用程序交互。通常,渲染一些帧是可行的
点击 Refresh 按钮, Observatory 会将 current trace buffer 从设备拉取过来。
温馨提示:当不知道怎么操作的时候,多看看右上角的?
按钮.
单击save按钮将使浏览器下载包含跟踪的JSON文件。您可以在bug报告或电子邮件中共享跟踪。要查看共享跟踪,请在Chrome中导航到about://tracing,并加载之前保存的跟踪文件。
Chrome中的跟踪查看器about://tracing也可以处理压缩的JSON文件。我发现这些更容易分享和工作。
引擎中最常用的跟踪事件类型是持续时间事件。这样的事件允许您在跟踪中注释代码块。因为它们不需要标识符,所以添加它们非常简单。在Dart中,您可以使用 dart:developer package’s Timeline 类来自己添加跟踪。Flutter engine & framework已经将持续时间事件添加到它认为重要的工作负载中。你也可以这样做。点击一个特定的持续时间,你就会看到花在该事件上的时间摘要。
Timeline.timeSync("Doing Something", () {
doSomething();
}
);
查找UI或GPU线程上的持续时间事件,它们是帧间隔的重要块。可以通过启用前面描述的Highlight Vsync按钮或者直接按v键来突出显示帧间隔。
如果您看到一个特别大的持续时间事件,下一步是突出显示代码的哪一部分对该块有贡献。当使用下面描述的采样分析器时,识别这样的块要容易得多。但是,如果您对所讨论的代码库有一定的了解,您也可以推测地向代码中添加跟踪。虽然只有在profile模式下才能收集到可靠的仪表号,但是我喜欢在debug模式下使用热重载来推测性地添加跟踪,以查看我是否离隔离瓶颈越来越近了.
单击事件将在底部的窗格中显示事件摘要。摘要的Events部分特别有用,因为它尝试连接所有逻辑上相关的持续时间事件。这些关系是使用下面描述的流事件推断出来的。Flutter框架和引擎已经为所有框架相关的工作负载添加了流事件。通过这种方式,您可以更容易地隔离与特定框架相关的所有工作(跨多个线程)。当您单击相关流的链接时,跟踪查看器将选择并突出显示所有连接的流。
在下面的示例中,选择所有相关的跟踪并按Self Time对列表排序,表明PhysicalShapeLayer::Paint trace是主导跟踪。可以看出这个跟踪是在GPU线程上,因为在摘要中对相同的图形进行鼠标拖动会突出显示相同的图形
一旦确定了这些主要的跟踪,我通常就知道应该深入研究代码的哪些部分。摘要也很有用,因为在跟踪过程中很容易在视觉上错过多次持续时间极短的小事件。但是这样的痕迹会在摘要中立即突出显示
有时,您需要描述生成持续时间跟踪的重复事件的性能,而不是单个事件。这可以通过拖动选择包含此类事件的时间轴部分来实现
trace viewerCatapult,可能有不同版本的Catapult嵌入在Chrome的about://tracing 和Observatory。我使用最新版本的Chrome浏览器,因为它有更多的最新跟踪查看功能。你也可以直接在GitHub上使用Catapult。如果您想查看在最新版本的Catapult中在Observatory中收集的跟踪,请将跟踪保存为JSON并在不同版本的Catapult中加载相同的跟踪。JSON格式是稳定的。
一旦您选择了一组跟踪,您应该会看到该选择中重复的所有跟踪的摘要。选择一个跟踪(在下面的例子中我使用的是最新版本的Catapult跟踪查看器),应该会显示有用的信息,比如每片的平均时间、标准偏差、重复计数等。
这将使您更好地了解您对代码库所做的改进,这些改进反映在重复事件(如帧)中较小的持续时间事件中。此外,一旦您确定了异常值,您就可以更容易地选择相同的异常值,并分析围绕相同异常值的跟踪,以解释偏差。
流事件用于逻辑地连接持续时间事件(可能在多个线程上)。在跟踪查看器中,它们显示为箭头。默认情况下,流事件会使跟踪视图非常混乱,并且被禁用。要启用相同的功能,请选中“View Options”中的“Flow events”框。流事件必须起源于持续时间事件。使用 Flow class in the dart:developer 包中Flow类可以自动完成这项工作。Flutter引擎还将一个称为“PipelineItem”的隐式流添加到所有帧相关的工作负载中。在上面描述的事件摘要中,所有与流计数相关的持续时间事件都是“相关的”。关联事件的后代也被计算在内。
在下面的例子中,在GPU线程渲染前,Flutter引擎正在UI线程上生成下一帧。如果没有流,就很难将持续时间事件与特定的框架工作负载关联起来。
通常,在添加跟踪时很难确定从哪里开始。每当引擎唤醒一个线程进行工作时,它都会添加一个名为MessageLoop::RunExpiredTasks的隐式持续时间跟踪。但是,在相同的跟踪中可能不会嵌套更多的跟踪。在这种情况下,采样分析器非常方便。Dart VM以特定的频率收集当前代码的回溯。无论何时运行任何Dart代码,这些示例都将在UI线程上显示为即时事件
samples很容易丢失,但非常方便。选择一个samples会在那个时间点显示backtrace和本机堆栈的回溯轨迹。如果您迷路了,不知道从哪里开始添加跟踪,只要继续选择一个示例,直到您识别出看起来很熟悉的代码。
这是一个简短的概述,您可以使用timeline工具提升您的应用程序。祝旅途快,现在才发现我开车,而且车速很快?
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。