n卡电脑性能优化

发布时间: 2023-04-17 11:52 阅读: 文章来源:转载
一、关于时间的优化

在实际开发中,我们必须努力让渲染引擎实现每秒60帧的刷新率。60 FPS意味着每帧之间大约有16毫秒的时间可以执行处理,这包括:将绘制原语数据上传到图形硬件所需的处理。

在实践中,我们应遵循以下规则:

(1)尽可能使用异步的事件驱动来编程。

(2)使用工作线程来完成重要的处理操作。

(3)不要手动自旋事件循环。

(4)在阻塞函数中,保证每帧花费的时间不超过几毫秒。

如果不遵循以上规则,极有可能导致一些帧被跳过,从而会对用户体验和显示上产生重大影响。

『注意』:当从QML调用C++代码时,尽量避免创建自己的QEventLoop或调用QCoreApplication::processEvents()函数。这是一个危险的用法,因为当在信号处理程序或绑定中进入事件循环时,QML引擎会继续运行其他绑定、动画、转换等,这些绑定会导致副作用,例如:破坏事件循环的层次结构。

二、关于JavaScript代码的优化

在绝大多数的QML应用程序中,将以动态函数、信号处理程序和属性绑定表达式的形式包含大量JavaScript代码。由于QML引擎的一些优化,比如:对绑定编译器所做的优化,它(在某些情况下)可以比调用C++函数更快。但是,在实际开发中也同样需要遵循一些规则。

(2-1)解析属性

在QML应用程序中,我们需要知道属性解析操作是需要时间的。虽然在某些情况下,解析出的结果可以缓存和重用,但如果可能的话,尽量避免或减少属性解析操作。

在下面例子中,有一个代码块多次使用rectid和color属性解析对象:

// bad.qmlimport QtQuick 2.3Item {width: 400height: 200Rectangle {id: rectanchors.fill: parentcolor: "blue"}function printValue(which, value) {console.log(which + " = " + value);}Component.onCompleted: {var t0 = new Date();for (var i = 0; i < 1000; ++i) {printValue("red", rect.color.r);printValue("green", rect.color.g);printValue("blue", rect.color.b);printValue("alpha", rect.color.a);}var t1 = new Date();console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations");}}

运行上述代码,在1000次迭代循环中,花费的时间如下:

可见,需要206毫秒的时间,但是我们可以优化这段代码,在代码块中只解析一次公共属性对象:

但上面代码还可以进一步优化(因为在循环处理过程中查找的属性是不会改变的),则可以将属性解析放到for循环外面,如下代码:

// better.qmlimport QtQuick 2.3Item {width: 400height: 200Rectangle {id: rectanchors.fill: parentcolor: "blue"}function printValue(which, value) {console.log(which + " = " + value);}Component.onCompleted: {var t0 = new Date();//将解析属性对象的操作放到for循环之外var rectColor = rect.color; for (var i = 0; i < 1000; ++i) {printValue("red", rectColor.r);printValue("green", rectColor.g);printValue("blue", rectColor.b);printValue("alpha", rectColor.a);}var t1 = new Date();console.log("Took: " + (t1.valueOf() - t0.valueOf()) + " milliseconds for 1000 iterations");}}(2-2)属性绑定

对于QML的属性绑定,如果属性绑定表达式引用的属性被更改,QML引擎会重新计算属性绑定表达式。因此,在实际使用中应该让绑定表达式应该尽可能简单

例如,如果有一个循环,在循环中会进行一些处理,但只有处理的最终结果是重要的,通常最好更新一个临时累加器,然后将其赋给需要更新的属性,而不是增量的更新属性本身,这样可以避免在累加的中间阶段触发绑定表达式的重新计算操作。

例如下列代码:

import QtQuick 2.3Item {id: rootwidth: 200height: 200property int accumulatedValue: 0Text {anchors.fill: parenttext: root.accumulatedValue.toString()onTextChanged: console.log("text binding re-evaluated")}Component.onCompleted: {var someData = [ 1, 2, 3, 4, 5, 20 ];for (var i = 0; i < someData.length; ++i) {accumulatedValue = accumulatedValue + someData[i];}}}

上述代码中,onCompleted处理程序中的循环会导致text属性绑定被重新计算六次(然后导致依赖文本值的其他所有的属性绑定,以及onTextChanged信号处理程序,每次都会被重新计算,并每次都要显示文本)。因此,这显然是不必要的,因为我们实际上只关心累加值的最终值。

我们可以优化代码如下:

import QtQuick 2.3Item {id: rootwidth: 200height: 200property int accumulatedValue: 0Text {anchors.fill: parenttext: root.accumulatedValue.toString()onTextChanged: console.log("text binding re-evaluated")}Component.onCompleted: {var someData = [ 1, 2, 3, 4, 5, 20 ];var temp = accumulatedValue;for (var i = 0; i < someData.length; ++i) {temp = temp + someData[i];}accumulatedValue = temp;}}三、序列技巧

在Qt QML中,有些序列类型是比较快速的(例如:QList, QList, QList, QList, QStringList和QList),相对来说其他的序列类型要慢许多。所使,在实际开发中应优先使用这些类型。

首先,序列类型有两种实现:

(1)一种是序列是QObject的Q_PROPERTY(称为引用序列)

(2)另一种是序列从QObjectQ_INVOKABLE函数返回(称为复制序列)。

通过QMetaObject::property()读取和写入引用序列,因此作为QVariant读取和写入。这意味着从JavaScript中改变序列中任何元素的值将导致三个步骤的发生:

1、完整的序列将从QObject(作为一个QVariant,但然后转换为正确类型的序列)。

2、位于指定索引处的元素将在该序列中更改

3、而完整的序列将被写回QObject(作为一个QVariant)。

复制序列要简单得多,因为实际序列存储在JavaScript对象的资源数据中,所以不会发生读/修改/写入周期(相反,直接修改资源数据)。

因此,写入引用序列的元素要比写入复制序列的元素慢得多。事实上,写入一个n元素引用序列的单个元素的代价相当于将一个n元素复制序列赋值给该引用序列,所以通常最好的做法是:修改一个临时复制序列,然后在计算期间将结果赋值给一个引用序列

假设存在以下C++类型(事先注册到“Qt.example 1.0”命名空间):

class SequenceTypeExample : public QQuickItem{Q_OBJECTQ_PROPERTY (QList qrealListProperty READ qrealListProperty WRITE setQrealListProperty NOTIFY qrealListPropertyChanged)public:SequenceTypeExample() : QQuickItem() { m_list
•••展开全文