2018年1月26日 vincent
对于一个程序的开发,性能优化是开发中的一个重要步骤。
我们肯定不希望开发出来的程序表现出卡顿,最好是处处流畅,丝滑般的体验。
对于C++程序,我们有很多方法可以做性能优化,例如Visual Studio Profiler。
而对于QML(QtQuick)程序,我们可以选择QML Profiler,这是QtCreator的一个功能。
那么QML Profiler是什么呢,官方的描述如下:
You can use the QML Profiler to find causes for typical performance problems in your applications, such as slowness and unresponsive, stuttering user interfaces. Typical causes include executing too much JavaScript in too few frames. All JavaScript must return before the GUI thread can proceed, and frames are delayed or dropped if the GUI thread is not ready.
也就是说,QML Profiler主要功能就是帮助我们去解决程序中典型的性能问题,说简单就是帮助我们做性能优化。
注意:这个性能优化,仅指QML这里,一般来说就是界面,可能还包含点界面逻辑代码(JS),而C++这块,QML Profiler几乎帮不上忙,最多是能给在QML中调用的槽函数记个耗时。
作为一名程序开发者,应该努力使渲染引擎的刷新率维持在60fps,也就是说在每帧之间大约有16ms,这段时间包括了基本图元在图形硬件上的描画。具体内容如下:
++如果不注意上面提到的内容,就会导致跳帧,影响用户体验。++
注意:
QML 与 C++ 交互时,为了避免阻塞就去创建自己的
QEventLoop 或调用QCoreApplication::processEvents(),
这虽说是一种常见的模式,但也是危险的,因为信号处理
或绑定进入事件循环时,QML 引擎会继续运行其它的绑定、
动画、状态迁移等,这些动作就可能带来副作用,例如,
破坏包含了事件循环的层级结构。
借助于QML Profiler,我们快速的了解程序运行中的主要情况和耗时细则(可以精确到微秒),其中包括但不限于:
QML Profiler的功能开放是从Qt5.7开始的,之前一直是企业版才有的,也就是花钱版。
选择debug模式:
启动工具后,等待程序运行起来,并且运行一段时间。然后点击Stop按钮,停止QML Profiler。
在这里,我们可以以时间轴角度,查看各个细节的耗时。时间轴的起点,就是QQmlApplication实例化的时间。我们可能看不到零点,因为在QQmlApplication被实例化到第一个元素被开始处理,时间可能会有其他的耗时。
在视图中,从左到右,就是QML Profiler从开始到停止的所有记录了。越小的块表示时间越短,反之越大的块,表示时间越长。这里的方块具有一定的嵌套关系,下面的方块对象隶属于上面的对象。比如说 Windows { } 里面还可能会有一个 Item { } 这样的嵌套关系。
看下面这个例子:
我们可以看出这里Image创建时间消耗78.7ms,对应的代码文件是main.qml和行数37行。
详细介绍:
在QML中使用的Image,默认是开缓存的。而所有缓存的图片,都会在这里显示,包括用了多少像素的缓存,还包括了图片的加载耗时、文件名等信息。(没有缓存的图片也会显示,但不会记入到缓存的阶梯里)
这里显示渲染时各个阶段的耗时,如果我们发现程序的动画有卡顿,除了一些函数的阻塞导致的卡顿外,还可以分析一下渲染的耗时开销,看看是不是渲染的量太大导致的卡顿。
这里我们主要关注Render Render这个数据,这个数据表示将OpenGL数据发送到GPU的过程。看到一个Render Render的结束,基本表示这一帧已经结束渲染,并且即将显示出来了。
另外还有Glyph Upload这个数据,这个数据表示字形纹路上传。如果你的程序是嵌入式,并且有很多的字,那么Glyph Upload有可能会带来一定的性能开销。减少这个开销的方式基本就是减少字,比如说用图片(Image)代替文字(Text或者Label)。
显示内存使用情况,如果这里有大块的内存增长,看看是不是这里在初始化很多东西,或者是有很多不必要的组件被创建出来了。
显示用户输入事件,例如鼠标和键盘事件
显示调试输出的时间点,如果你需要对照Debug输出和对应的QML事件,那么这会很有帮助
显示是否有动画在执行,以及动画的FPS,在多线程渲染时还会显示多线程的信息。如果我们发现FPS低于18,那么视觉上可能就会有明显的卡顿了。而30到60的FPS,一般就可以认为是流畅的。
显示编译的耗时。这里要说下,从Qt5.8开始,QtQuick引入了qmlc机制,让编译时间大幅度缩减,基本上是从几百毫秒,缩减到几十甚至十毫秒以内。之前在csdn发过文章讲这个,这里再放下链接:
显示创建的耗时,一般也是启动优化的主要部分
显示绑定的耗时
显示信号处理的耗时
显示JS执行的耗时。如果在QML里调用了一个C++的槽,那么这里也会有计时,但是也只有槽函数的总耗时,C++那里的运行情况这里看不出来。
选择统计Statistic Tab如下:
在这里,我们可以看到每个细则,例如编译、创建、绑定、JavaScript或者信号处理的次数以及它们所消耗的时间。
除了在时间轴那里,通过肉眼观察,我们在这里,通过对百分比的排序,也可以迅速的看出哪个东西最耗时。
选择Flame Graph Tab。
在这里,我们可以看到更加简洁的QML和JS统计。其中也直观的告诉了我们一些嵌套关系。
综上,这是最基本的3个功能区,构成了QML Profiler。我们程序的性能分析,主要也围绕着这三点展开。
如果程序有明显的加载慢问题,那么可以先去看创建,找大块,去延后加载或者异步加载。让首界面先显示出来。尤其是图片,图片的加载比较慢,尽量选择合适分辨率的图片,不要过大。对于不会再第一时间显示的东西,尽量不要在第一时间加载。
如果程序有明显卡顿问题,那么可以看渲染那里,是不是渲染的东西太多了,例如用了过多的clip。或者有很多在视觉上看不到的元素,例如xy为-1000这样的Item,没有被隐藏,这些Item照样会渲染,照样会有性能开销,对于这些元素可以将visible设置为false,直接影藏掉,这就不会有渲染耗时了。例外值得一提的是,对于有动画的场景,建议把每帧时间控制在16ms以内,以维持60FPS的流畅界面。
关于性能优化进一步的细节点,这里不展开,以后单独发文章讲,本文只讲QML Profiler的基础。更多关于QML Profiler的信息,可以前往官网查看:
Profiling QML Applications