本页内容

QML Profiler 应用程序的性能分析

借助QML Profiler ,您可以找出应用程序中典型性能问题的根源,例如运行缓慢、无响应以及用户界面卡顿。典型原因包括在过少的帧数内执行了过多的JavaScript代码。所有JavaScript代码必须先返回,GUI线程才能继续执行;如果GUI线程尚未准备就绪,帧就会被延迟或丢弃。

导致此类性能问题的另一个典型原因是创建或更新不可见项,这会在GUI线程中消耗时间。

触发长时间运行的 C++ 函数(例如绘制方法和信号处理程序)也会在 GUI 线程中消耗时间,但在QML Profiler 中较难察觉,因为该工具不分析 C++ 代码。

要查找 JavaScript 的过度使用,请检查动画和场景图事件中的帧率,查找间隙,并检查应用程序是否按预期运行。JavaScript 类别会显示函数的运行时间,您应尽量将其控制在每帧 16 毫秒以内。

要查找因处理不可见项而导致的问题,请查找丢帧情况,并检查是否使用了过多的短绑定或每帧更新的信号处理程序。您还可以可视化场景图的过度绘制,以检查场景布局,并找出因位于屏幕外或被其他可见元素遮挡而用户永远无法看到的项。

如果即使未运行 JavaScript 仍出现帧丢失,且时间轴上存在无法解释的大段空隙,请检查您的自定义 `QQuickItem ` 实现。您可以使用Valgrind Callgrind或其他通用分析工具来分析 C++ 代码。

您可以使用全栈跟踪,从顶层的 QML 或 JavaScript 开始,一直追踪到 C++ 乃至内核空间。您可以在Chrome 跟踪格式查看器中查看收集到的数据。

分析收集的数据

Timeline 视图以图形化方式展示QML和JavaScript的执行情况,并提供所有记录事件的摘要视图。

QML Profiler

时间轴(6)中的每一行描述了一种已记录的 QML 事件类型。将光标悬停在某行上的事件上,即可查看该事件的耗时及其在源代码中的调用位置。若仅在选中事件时显示相关信息,请关闭“View Event Information on Mouseover ”(4)。

大纲 (10) 总结了数据收集的时间段。拖动缩放范围 (8) 或单击大纲可在大纲中移动。您还可以通过选择“Jump to Previous Event ”和“Jump to Next Event ”(1)在事件之间切换。

选择“Show Zoom Slider ”(2)可打开用于设置缩放级别的滑块。您也可以拖动缩放控点(9)。要重置默认缩放级别,请右键单击时间轴以打开上下文菜单,然后选择“Reset Zoom ”。

选中时间标尺,即可在时间轴上添加垂直参考线 (5)。

选择事件范围

选择一个事件范围 (7),以查看事件的帧率,并将其与类似事件的帧率进行比较。 选择“Select Range ”(3)以激活选择工具。然后在时间轴上单击以指定事件范围的起始点。拖动选择控点以定义范围的结束点。该范围的长度即表示该事件的帧率。

要测量两个连续事件之间的延迟,请在第一个事件的结束点与第二个事件的开始点之间设置一个事件范围。“Duration ”字段将以毫秒为单位显示事件之间的延迟。

要放大事件范围,请双击该范围。

要在“Timeline ”、“Statistics ”和“Flame Graph ”视图中缩小当前范围,请右键单击该范围并选择“Analyze Current Range ”。要返回完整范围,请在上下文菜单中选择“Analyze Full Range ”。

要删除事件范围,请关闭“Selection ”对话框。

理解数据

通常,时间轴视图中的事件表示 QML 或 JavaScript 的执行耗时。将鼠标悬停在事件上可查看详细信息。对于大多数事件,其详细信息包含源代码中的位置、持续时间以及源代码本身的相关部分。

选择一个事件,即可将代码编辑器中的光标移至与该事件相关的代码位置。

时间轴视图中会在一行或多行上显示以下类型的事件。

事件类别描述
Pixmap Cache显示缓存的像素图数据总量(以像素为单位)。此外,还会为正在加载的每张图片显示一个单独的事件,其中包含其文件名和大小等具体信息。
Scene Graph显示场景图帧的渲染时间,以及为此执行的各个阶段的一些附加计时信息。
Memory Usage显示 JavaScript 内存管理器的内存块分配情况。通常,内存管理器会预留一块较大的内存块,随后将其分批分配给应用程序。如果应用程序请求的单个内存块超过一定大小,内存管理器将单独分配这些内存块。这两种操作模式以不同颜色的事件显示。 第二行显示已分配内存的实际使用情况。这是应用程序实际请求的 JavaScript 堆内存量。
Input Events显示鼠标和键盘事件。
Painting未使用。
Animations显示当前活跃的动画数量及其运行帧率。渲染线程中的动画会在单独一行中显示。
Compiling显示编译 QML 文件所花费的时间。
Creating显示在场景中创建元素所花费的时间。元素的创建分为两个阶段。第一阶段用于创建数据结构,包括子元素。第二阶段代表完成回调。不过,并非所有元素都会触发完成回调。这两个阶段在时间轴上显示为独立的事件。
Binding显示绑定被求值的时间以及求值所耗费的时间。
Handling Signal显示信号被处理的时间以及处理所耗时长。
JavaScript显示用于执行绑定和信号处理程序背后实际 JavaScript 代码所花费的时间。它列出了您可能用于评估绑定或处理信号的所有 JavaScript 函数。
Quick3D显示渲染Qt Quick 3D帧所花费的时间、帧准备和同步的计时信息、粒子系统更新时间及粒子更新次数,以及纹理和网格的内存分配与内存消耗情况。

此事件类型自 Qt 6.3 起可用。

分析场景图事件

要了解场景图类别,请阅读Qt Quick Scene GraphQt Quick Scene Graph Default Renderer,进一步了解Qt Quick 场景图的工作原理。以下事件会在“Scene Graph ”类别中报告。并非所有事件都会在所有渲染循环中生成。在 Windows 和 Basic 渲染循环中,所有操作都在同一线程中运行,因此 GUI 线程与渲染线程之间的区分没有意义。

设置环境变量 QSG_RENDER_TIMING,可获得与被分析应用程序类似但略有差异的文本形式计时输出。具体差异如下所示。

事件类型线程渲染循环类型QSG_RENDER_TIMING 输出中的标签描述
PolishGUI多线程、基本、Windowspolish在使用QQuickItem::updatePolish() 渲染项目之前对其进行最终润色。
GUI Thread WaitGUI多线程lock执行连接到QQuickWindow::afterAnimating() 信号的槽,然后锁定渲染线程的互斥锁,随后在GUI Thread Sync 上等待同一互斥锁。如果此操作在Render Thread Sync 之前很久就开始,那么 GUI 线程中就会有空闲时间,您可以利用这些时间运行额外的 QML 或 JavaScript。
GUI Thread SyncGUI多线程blockedForSyncGUI 线程因等待渲染线程而被阻塞的时间。
AnimationsGUI多线程,Windowsanimations在GUI线程中推进动画。基本渲染循环不会使动画与渲染保持同步。这就是为什么在使用基本渲染循环时不会显示任何动画事件。请观察Animations 类别,以查看此情况下的动画时机。
Render Thread Sync渲染多线程,基本,WindowsFrame rendered ... sync使用QQuickItem::updatePaintNode() 将 QML 状态同步到场景图中。
Render渲染多线程、基本、WindowsFrame rendered ... render渲染帧所耗费的总时间,包括准备并将所有必要数据上传至 GPU 的时间。这是渲染时间。请勿将其与下文所述的 Render Render 时间混淆。
Swap渲染多线程、基本、WindowsFrame rendered ... swap渲染后的帧交换。
Render Preprocess渲染多线程、基础、Windowstime in renderer ... preprocess在所有需要预处理的节点上调用QSGNode::preprocess()。这是“Render ”步骤的一部分。
Render Update渲染多线程、基础、Windowstime in renderer ... updates遍历并处理场景图中的所有节点,以更新其几何体、变换、不透明度及其他状态。在Render Thread Sync 阶段,每个节点都会根据GUI线程提供的状态单独更新;而在Render Update 阶段,所有节点将合并以生成最终场景。这是“粗略渲染”Render 步骤的一部分。
Render Bind渲染多线程、基础、Windowstime in renderer ... binding为 OpenGL 渲染绑定正确的帧缓冲区。这是“Render ”步骤的一部分。
Render Render渲染多线程、基础、Windowstime in renderer ... rendering通过 OpenGL 将所有数据发送至 GPU 的实际过程。这是“Render ”步骤的一部分。
Material Compile渲染多线程、基础、Windowsshader compiled编译GLSL着色器程序。
Glyph Render渲染多线程、基础、Windowsglyphs ... rendering将字体字形渲染到纹理中。
Glyph Upload渲染多线程、基础、Windowsglyphs ... upload将字形纹理上传至 GPU。
Texture Bind渲染多线程、基础、Windowsplain texture ... bind使用 glBindTextures 在 OpenGL 上下文中绑定纹理。
Texture Convert渲染多线程、基础、Windowsplain texture ... convert转换图像格式并缩小图像尺寸,使其适合用作纹理。
Texture Swizzle渲染多线程、基础、Windowsplain texture ... swizzle如有必要,在 CPU 上对纹理数据进行 Swizzling 操作。
Texture Upload渲染多线程、基础、Windowsplain texture ... upload / atlastexture uploaded将纹理数据上传至 GPU。
Texture Mipmap渲染多线程、基础、Windowsplain texture ... mipmap在 GPU 上对纹理进行 Mip 映射。
Texture Delete渲染多线程、基础、Windowsplain texture deleted从 GPU 中删除不再需要的纹理。

分析Qt Quick 3D事件

以下是Qt Quick 3D的事件列表。每个渲染帧由同步、准备和渲染三个阶段组成,这些阶段按此顺序执行。同步发生在场景图同步阶段,而准备和渲染则发生在场景图渲染阶段。

设置环境变量QSG_RENDERER_DEBUG=render ,可获取不同渲染通道的渲染调用次数的额外文本输出。这些调用次数会在“渲染帧”事件中进行汇总。

事件类型线程描述
Render Frame渲染帧的渲染时间。同时显示绘制调用次数。
Prepare Frame渲染准备一帧所需的时间。资源在准备阶段进行分配和加载。场景加载后的第一帧通常比其他帧耗时更长,因为此时需要加载大部分资源。
Synchronize Frame渲染帧的同步时间。同步负责将前端数据更新至后端,并管理Qt Quick 场景图与Qt Quick 3D之间的共享资源。
Mesh Load渲染网格的加载时间。显示所有网格的总内存占用情况。同时显示卸载情况。
Custom Mesh Load渲染自定义网格的加载时间。显示所有网格的总内存占用情况。同时显示卸载情况。
Texture Load渲染纹理的加载时间。显示所有纹理的总内存占用量。同时显示卸载情况。
Generate Shader渲染为材质生成着色器所需的时间。
Load Shader渲染加载内置着色器所需的时间。
Particle UpdateGUI粒子系统的更新时间。显示已更新的粒子数量。
Mesh Memory Consumption渲染以条形图形式显示总网格内存占用情况。
Texture Memory Consumption渲染以条形图形式显示纹理内存的总占用量。
Render Call渲染单次绘制或计算调用的耗时。有助于识别耗费大量GPU资源的操作。
Render Pass渲染整个渲染通道所耗时间。显示针对帧缓冲区的分组渲染操作。

查看统计信息

Statistics ”视图会显示每次绑定、创建、编译、JavaScript 或信号事件的触发次数及其平均耗时。通过分析这些统计数据,可以确定需要优化的事件。触发次数过多可能表明该事件被不必要地触发了。 要查看这些事件的中位数、最长和最短时间,请在上下文菜单中选择“Extended Event Statistics ”。

选择某个事件,即可在代码编辑器中跳转至源代码中的该位置。

统计视图

Callers “事件图”(Callees )和“绑定图”( )展示了事件之间的依赖关系。它们允许您检查应用程序的内部功能。“绑定图”(Callers )汇总了触发某个绑定的 QML 事件。这告诉您是什么导致了绑定的变化。“绑定图”(Callees )汇总了某个绑定触发的 QML 事件。这告诉您,如果您更改某个绑定,哪些 QML 事件会受到影响。

选择一个事件,即可在代码编辑器中跳转至源代码中的该位置。

当您在“Timeline ”视图中选中某个事件时,相关信息会显示在“Statistics ”和“Flame Graph ”视图中。

要将某个视图或行中的内容复制到剪贴板,请在上下文菜单中选择“Copy Table ”或“Copy Row ”。

以火焰图形式可视化统计数据

Flame Graph ”视图以更简洁的方式展示了 QML 和 JavaScript 执行的统计概览。在“Total Time ”视图中,水平条形图显示了某个函数所有调用的总耗时,相对于所有 JavaScript 和 QML 事件的总运行时间。嵌套关系显示了哪些函数被哪些其他函数调用。

火焰图视图

若要查看函数分配的总内存量,请在下拉菜单中选择“Memory ”。

要查看函数执行的内存分配次数,请选择“Allocations ”。

双击视图中的某个项目可对其进行放大。双击视图中的空白区域可再次缩小。

与“Timeline ”视图不同,“Flame Graph ”视图不会显示完全没有 QML 或 JavaScript 运行的时间段。因此,它不适合分析每帧的执行时间。不过,在此视图中可以非常轻松地查看各种 QML 和 JavaScript 事件的总体影响。

另请参阅 《如何:分析》《分析器》、《分析代码》以及《QML 应用程序的性能分析》。

Copyright © The Qt Company Ltd. and other contributors. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.