QML应用程序分析
您可以使用QML性能分析器来找到应用程序中典型性能问题的原因,如速度慢、无响应、界面卡顿。常见原因包括在很小的帧中执行过多的JavaScript。所有JavaScript都必须在GUI线程继续之前返回,如果GUI线程没有准备好,帧将被延迟或丢弃。
导致类似性能问题的另一个常见原因是创建、绘制或更新不可见项,这在GUI线程中会花费时间。
为了找到过度使用JavaScript,请在动画和场景图事件中检查帧率,寻找差距,并检查应用程序是否按预期运行。JavaScript类别显示了函数的运行时间,您应该尝试将其保持在每帧16毫秒以下。
为了找到由处理不可见项引起的问题,寻找丢帧并检查您是否没有使用太多的短绑定或每帧更新的信号处理器。您还可以可视化场景图重绘以检查场景布局,并找出用户永远不会看到的项目,因为这些项目位于屏幕外或被其他可见元素隐藏。
注意:在本节中,您正在使用高级菜单项。默认情况下它们是不可见的。要切换高级菜单项的可见性,请参阅自定义菜单栏。
使用QML性能分析器
要在QML性能分析器中监视应用程序的性能
- 要能够对应用程序进行性能分析,您必须为项目设置QML调试。有关更多信息,请参阅设置QML调试。
注意:要设备上的应用程序进行性能分析,您必须在设备上安装Qt库。
- 选择 分析 > QML性能分析器 以对当前应用程序进行性能分析。
- 选择(开始)按钮从QML分析器启动应用程序。
注意:如果数据收集没有自动启动,请选择(启用分析)按钮。
当您开始分析应用程序时,应用程序将启动,QML分析器立即开始收集数据。这由已耗时字段中运行的计时器表示。
数据将持续收集,直到您选择启用分析按钮。数据收集需要时间,因此,在数据显示之前可能会有延迟。
请勿使用应用程序命令退出应用程序,因为当您选择启用分析按钮时,数据会发送到QML分析器。应用程序将继续运行几秒钟后自动停止。如果您退出应用程序,数据将不会发送。
选择禁用分析按钮以禁用应用程序启动时的数据收集自动启动。重新选择按钮时将开始数据收集。
要保存所有收集到的数据,请选择分析 > QML分析器选项 > 保存QML跟踪。要查看保存的数据,请选择加载QML跟踪。您还可以与他人共享保存的数据或加载他们保存的数据。
指定刷新设置
您可以为QML分析器指定全局设置,适用于所有项目,或为每个项目单独指定。要指定全局设置,请选择首选项 > 分析器。
要指定特定项目的自定义QML分析器设置,请选择项目 > 运行,然后在QML分析器设置中选择自定义。要恢复项目的全局设置,请选择恢复全局。
选择分析时刷新数据复选框以定期刷新数据而不是在分析停止时刷新所有数据。这可以在目标设备上节省内存并且减少了分析停止和数据显示间的等待时间。
在刷新间隔字段中,设置以毫秒为单位的刷新间隔。间隔越短,刷新次数越多。间隔越长,目标应用程序中需要缓冲的数据越多,这可能会浪费内存。然而,刷新本身也需要时间,这可能会扭曲分析结果。
如果您有多个QML引擎并且您想要将它们产生的所有数据汇总到一个跟踪中,请选择仅当进程结束时处理数据复选框。否则,当其中一个引擎停止时,分析将停止。
附加到运行中的Qt Quick应用程序
您可以对未由Qt设计工作室启动的Qt Quick应用程序进行分析。但是,您必须在项目的构建设置中启用QML调试和分析。更多信息,请参见设置QML调试。
要附加到等待的应用程序
- 选择分析 > QML分析器(附加到等待应用程序)。
- 在工具包中,选择用于构建应用程序的工具包。
- 在端口中,指定要监听的端口。
- 选择确定。
分析收集到的数据
时间线视图显示QML和JavaScript执行的图形表示和所有记录事件的压缩视图。
时间表(6)中的每一行描述了一种已记录的 QML 事件类型。将光标移至行中的事件上,可以看到该事件持续的时间和它在源代码中的调用位置。要仅在选中事件时显示信息,请禁用鼠标悬停时查看事件信息按钮(4)。
概述(10)总结了收集数据的时期。拖动缩放范围(8)或单击概述来在其上移动。您还可以通过选择跳转到前一个事件和跳转到下一个事件按钮(1)在事件之间移动。
选择显示缩放滑块按钮(2)以打开一个滑块,您可以使用它设置缩放级别。您还可以拖动缩放把手(9)。要重置默认缩放级别,右键单击时间表以打开上下文菜单,然后选择重置缩放。
单击时间刻度来向时间表添加垂直方向线(5)。
选择事件范围
您可以选择事件范围(7)以查看事件的帧率,并将其与相似事件的帧率进行比较。选择选择范围按钮(3)以激活选择工具。然后在时间表中单击以指定事件范围的起始位置。拖动选择把手来定义范围的结束。范围的长度表示事件的帧率。
您还可以使用事件范围来测量两个后续事件之间的延迟。在一个事件的结尾和一个事件的开始之间放置范围。时间延迟(毫秒)字段显示事件的延迟。
要放大事件范围,请双击它。
要在时间表、统计信息和火焰图中缩小当前范围的当前范围,右键单击范围并选择分析当前范围。要返回完整范围,请在上下文菜单中选择分析完整范围。
要删除事件范围,请关闭选择对话框。
理解数据
通常,时间视图中的事件表示 QML 或 JavaScript 执行的持续时间。将鼠标悬停在其上以查看详细信息。对于大多数事件,它们包括源代码中的位置、持续时间和源代码本身的相关部分。
您可以通过单击事件来将代码编辑器中的光标移到与事件相关联的代码部分。
以下类型的事件显示在时间视图的一行或多行上。事件类型的可用性取决于应用程序编译时所使用的 Qt 版本以及它所使用的 Qt Quick 版本。
事件类别 | 描述 | 最小 Qt 版本 | Qt Quick 版本 |
---|---|---|---|
Pixmap Cache | 显示缓存像素数据的总量。此外,为每个正在加载的图片显示单独的事件,并提供有关其文件名和大小的详细信息。 | Qt 5.1 | Qt Quick 2 |
Scene Graph | 显示场景图框架渲染的时间以及执行这些阶段的一些额外时间信息。 | Qt 5.1 | Qt Quick 2 |
内存使用 | 显示 JavaScript 内存管理器的块分配。通常,内存管理器将保留大小较大的单个内存块,并稍后以较小的碎块的形式将其分配给应用程序。如果应用程序请求超过一定大小的单个内存块,则单独分配这些内存。这两种操作模式以不同颜色的不同事件显示。第二行显示分配的内存的实际使用情况。这是应用程序实际请求的 JavaScript 堆的大小。 | Qt 5.4 | Qt Quick 2 |
输入事件 | 显示鼠标和键盘事件。 | Qt 4.7.4 | Qt Quick 1 或 Qt Quick 2 |
绘图 | 显示每帧场景绘制的耗时。 | Qt 4.7.4 | Qt Quick 1 |
动画 | 显示活动动画的数量和执行时的帧率。对于使用 Qt 5.3 或更新版本构建的应用程序,会显示渲染线程动画的信息。渲染线程动画在单独的行显示。 | Qt 5.0 (Qt 5.3) | Qt Quick 2 |
编译 | 显示编译 QML 文件所花费的时间。 | Qt 4.7.4 | Qt Quick 1 或 Qt Quick 2 |
创建 | 显示创建场景中元素所花费的时间。在 Qt Quick 2 中,元素的创建分为两个阶段。第一阶段是创建数据结构(包括子元素)。第二阶段代表完成回调。虽然不是所有元素都会触发完成回调,但在时间线上会将阶段单独列出。对于使用版本低于 5.2.1 的 Qt 编译的 Qt Quick 2 应用程序,只显示顶层元素的创建,作为单个事件。 | Qt 4.7.4 (Qt 5.2.1) | Qt Quick 1 或 Qt Quick 2 |
绑定 | 显示绑定的评估时间和评估耗时。 | Qt 4.7.4 | Qt Quick 1 或 Qt Quick 2 |
处理信号 | 显示处理信号的时间和处理耗时。 | Qt 4.7.4 | Qt Quick 1 或 Qt Quick 2 |
JavaScript | 显示执行绑定和信号处理后面的实际 JavaScript 的时间。列出所有用于评估绑定或处理信号的 JavaScript 函数。 | Qt 5.3 | Qt Quick 2 |
Quick3D | 显示绘制 Qt Quick 3D 帧所花费的时间,包括帧准备和同步的时间信息,粒子系统更新时间和粒子更新次数,以及纹理和网格内存分配和内存消耗。 | Qt 6.3 | Qt Quick 3D |
分析场景图事件
为了理解场景图类别,了解 Qt Quick 场景图是如何工作的重要。请参阅Qt Quick 场景图和Qt Quick 场景图默认渲染器获取详细描述。以下事件在场景图类别中报告。并非所有事件都由所有渲染循环生成。在 Windows 和基本渲染循环中,所有操作都在同一个线程中运行,GUI 线程和渲染线程之间的区别没有意义。
如果您设置了环境变量 QSG_RENDER_TIMING,您将获得待分析应用程序类似但略有不同的时间文本输出。以下列出差异,以便于定位。
事件类型 | 线程 | 渲染循环类型 | 输出中的标签 QSG_RENDER_TIMING | 描述 | 注意事项 |
---|---|---|---|---|---|
润色 | GUI | 线程化,基本,Windows | polish | 使用 QQuickItem::updatePolish 对即将渲染的项目进行最终润色。 | Qt 5.4 之前的 Qt 版本记录的基本渲染循环没有润色时间,Windows 渲染循环有错误的润色时间。 |
GUI 线程等待 | GUI | 线程化 | lock | 执行连接到 QQuickWindow::afterAnimating 信号的槽,然后在等待 GUI 线程同步之前锁定渲染线程的互斥量。如果这在大约 渲染线程同步 之前开始,您可以在 GUI 线程中有 空闲 时间来运行额外的 QML 或 JavaScript。 | None |
GUI 线程同步 | GUI | 线程化 | blockedForSync | GUI 线程阻塞等待渲染线程的时间。 | None |
动画 | GUI | 线程化,Windows | animations | 在GUI线程中推进动画。基本的渲染循环并不能使动画与渲染同步。这就是为什么在使用基本渲染循环时不会有任何动画事件显示。请查看动画类别以查看在这种情况下动画的时间。 | None |
渲染线程同步 | 渲染 | 线程化,基本,Windows | 渲染帧 ... 同步 | 使用 QQuickItem::updatePaintNode() 将QML状态同步到场景图。 | None |
渲染 | 渲染 | 线程化,基本,Windows | 渲染帧 ... 渲染 | 渲染帧所花费的总时间,包括准备以及将所有必要数据上传到GPU。这是毛渲染时间。请不要与下面的净 渲染渲染时间混淆。 | 在Qt 5.5之前版本的Qt中,由于使用不同的、不同步的计时器来测量,毛渲染时间和下面的渲染时间分额可能会因微秒级的偏差而不匹配。例如,渲染预处理可能看起来在渲染线程同步完成之前就开始。 |
交换 | 渲染 | 线程化,基本,Windows | 渲染帧 ... 交换 | 在渲染后交换帧。 | 当使用QSG_RENDER_TIMING设置触发交换时间时,基本渲染循环以及Qt 5.4之前版本的Qt中报告的交换时间是不正确的。QML配置文件显示正确的交换时间。 |
渲染预处理 | 渲染 | 线程化,基本,Windows | 在渲染器中的时间 ... 预处理 | 对需要预处理的全部节点调用QSGNode::preprocess()。这是毛渲染步骤的一部分。 | 与版本低于Qt 5.5的Qt的渲染可能不完全对齐。 |
渲染更新 | 渲染 | 线程化,基本,Windows | 在渲染器中的时间 ... 更新 | 迭代并处理场景图中的所有节点以更新它们的位置,变换,透明度以及其他状态。在渲染线程同步阶段,每个节点都使用来自GUI线程的状态单独更新。在渲染更新中,所有节点被组合以创建最终的场景。这是毛渲染步骤的一部分。 | 与版本低于Qt 5.5的Qt的渲染可能不完全对齐。 |
渲染绑定 | 渲染 | 线程化,基本,Windows | 在渲染器中的时间 ... 绑定 | 绑定正确的帧缓冲区以进行OpenGL渲染。这是毛渲染步骤的一部分。 | 与版本低于Qt 5.5的Qt的渲染可能不完全对齐。 |
渲染渲染 | 渲染 | 线程化,基本,Windows | 在渲染器中的时间 ... 渲染 | 通过OpenGL将所有数据发送到GPU的实际过程。这是毛渲染步骤的一部分。 | 与版本低于Qt 5.5的Qt的渲染可能不完全对齐。 |
材质编译 | 渲染 | 线程化,基本,Windows | 着色器已编译 | 编译GLSL着色器程序。 | None |
字形渲染 | 渲染 | 线程化,基本,Windows | 字形 ... 渲染 | 将字体字形渲染到纹理中。 | Qt 5.4之前的版本为这些事件报告了不正确的时间。 |
字形上传 | 渲染 | 线程化,基本,Windows | 字形 ... 上传 | 上传字形纹理到GPU。 | Qt 5.4之前的版本为这些事件报告了不正确的时间。 |
纹理绑定 | 渲染 | 线程化,基本,Windows | 平面纹理 ... 绑定 | 在OpenGL上下文中使用glBindTextures绑定纹理。 | None |
纹理转换 | 渲染 | 线程化,基本,Windows | 平面纹理 ... 转换 | 转换格式并且将图像下采样以便可以用作纹理。 | None |
纹理混色 | 渲染 | 线程化,基本,Windows | 平面纹理 ... 混色 | 如有必要,在CPU上混色纹理数据。 | None |
纹理上传 | 渲染 | 线程化,基本,Windows | 平面纹理 ... 上传 / atlastexture 已上传 | 将纹理数据上传到GPU。 | None |
纹理米波映射 | 渲染 | 线程化,基本,Windows | 平面纹理 ... 米波映射 | 在GPU上对纹理进行米波映射。 | None |
纹理删除 | 渲染 | 线程化,基本,Windows | 平面纹理删除 | 删除不再必要的纹理从GPU。 | None |
分析Qt Quick 3D事件
以下是Qt Quick 3D的事件列表。每个渲染帧都由同步、准备和渲染阶段组成,依次完成。同步发生在场景图同步阶段,而准备和渲染则发生在场景图渲染阶段。
设置环境变量 QSG_RENDERER_DEBUG=render
也会提供不同渲染过程渲染调用次数的额外文本输出。这些调用次数在 Render Frame 事件中汇总。
事件类型 | 线程 | 描述 |
---|---|---|
渲染帧 | 渲染 | 帧的渲染时间。同时显示绘制调用次数。 |
准备帧 | 渲染 | 准备帧所需的时间。资源在准备阶段分配和加载。场景加载后的第一帧通常比其他帧花费的时间更长,因为在那个时候,大多数资源都会被加载。 |
同步帧 | 渲染 | 帧的同步时间。同步负责从前端更新后端值。还管理 Qt Quick 场景图和 Qt Quick 3D 之间的共享资源。 |
网格加载 | 渲染 | 网格的加载时间。显示所有网格的总内存使用量。也显示卸载操作。 |
自定义网格加载 | 渲染 | 自定义网格的加载时间。显示所有网格的总内存使用量。也显示卸载操作。 |
纹理加载 | 渲染 | 纹理的加载时间。显示所有纹理的总内存使用量。也显示卸载操作。 |
生成着色器 | 渲染 | 为材质生成着色器所需的时间。 |
加载着色器 | 渲染 | 加载内建着色器所需的时间。 |
粒子更新 | GUI | 粒子系统的更新时间。显示更新粒子的数量。 |
网格内存消耗 | 渲染 | 显示总网格内存消耗的条形图。 |
纹理内存消耗 | 渲染 | 显示总纹理内存消耗的条形图。 |
查看统计
统计 视图显示每个绑定、创建、编译、JavaScript 或信号事件被触发的次数及其平均耗时。这允许您检查需要优化的哪个事件。发生次数很多可能表明事件是不必要地被触发的。要查看中位数、最长时间和最短时间,请在上下文菜单中选择 扩展事件统计。
在代码编辑器中点击事件可跳转到源代码中的该事件。
调用者 和 被调用者 面板显示事件之间的依赖关系。它们允许您检查应用程序的内部函数。调用者面板总结了触发绑定的 QML 事件。这告诉您是什么引起绑定发生变化。被调用者面板总结了绑定引发的 QML 事件。这告诉您,如果您更改绑定,哪些 QML 事件受到影响。
在代码编辑器中点击事件可跳转到源代码中的该事件。
当您在 时间轴 视图中选择事件时,相关信息将在 统计 和 火焰图 视图中显示。
要复制一个视图或行的内容到剪贴板,请在上下文菜单中选择 复制表格 或 复制行。
对于使用 Qt Quick 2 并使用 Qt 5.3 或更高版本编译的应用程序,统计视图中仅显示 JavaScript 事件。
将统计分析可视化成火焰图
火焰图 视图显示了 QML 和 JavaScript 执行的更简洁的统计概览。在 总时间 视图中,水平条显示了一定函数所有调用所需的总时间,相对于所有 JavaScript 和 QML 事件的总运行时间。嵌套显示是哪一些函数被哪些其他函数调用。
要查看函数分配的总内存量,请选择下拉菜单中的 内存。
要查看函数执行的内存分配次数,请选择 分配。
双击视图中的项可放大查看。双击视图中的空白区域可再次放大退出。
与时间轴视图不同,火焰图视图不会显示没有任何QML或JavaScript运行时的时段。因此,它不适用于分析每帧执行时间。然而,很容易在那里看到各种QML和JavaScript事件的总影响。
在特定的Qt许可证下可用。
了解更多信息。