Qt performance optimization: high CPU occupancy phenomenon and solutions

I. Introduction

In the project, it was found that when the Qt program was executed, the CPU usage rate was extremely high in some cases, up to 100%. The project runs on an embedded board. At the beginning, the EGLFS plug-in was used, but because the board does not have a separate mouse layer, the mouse moves jerky and is not smooth, so it was replaced by the LinuxFB plug-in. However, if the CPU occupancy rate is high, it will also cause the mouse to freeze, because the mouse is drawn by the Qt application layer. If the CPU occupancy rate of the application is high, it will also cause the mouse to draw slowly and cause the mouse to freeze.

Therefore, to solve the problem of high CPU occupancy and optimize performance, this article records the problems and solutions that are prone to occur in the project.

2. Frequent refresh of UI controls

Phenomenon

UI controls (including QWidget) controlled by code logic are refreshed frequently, or an action is executed frequently. UI refresh will affect the CPU usage to a certain extent. If the control is refreshed frequently (whether it is redrawing or updating data), it will consume a lot of CPU usage. For example, a time Label uses a timer to set the time. If the timer interval is too short, such as 10ms or even shorter, it will consume more CPU usage.

Solution

Therefore, when customizing UI controls, you need to pay attention to avoid frequent refreshes; the interval of the timer should also be set reasonably to avoid being too short or too long.

Three, paintEvent high-frequency drawing

This situation is very common, but it is inconvenient to put a lot of related content here. You can read my next blog: Qt performance optimization 2: drawing video scheme selection

4. Optimize interface refresh

Phenomenon
There is a function in the project to read data in the sub-thread, if the data changes, send a signal, and update the interface in the slot. When the program is running, the data changes frequently, which also causes the signal to update the interface to be triggered many times in a short period of time, which greatly reduces the performance of the program.

Solution
Because the frame rate captured by the human eye is limited, the interface display does not need to refresh too fast. Changing the signal slot update interface to use a timer to update the interface can avoid the lag caused by excessive data changes in a certain period of time. question.

The benefits of this article, free to receive Qt development learning materials package, technical video, including (C++ language foundation, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

5. The style sheet causes the drawing event to be called frequently

Phenomenon

QWidget: the main window, the background is set by setStyleSheet, and the paintEvent event is rewritten (the paintEvent of QWigdet is empty by default):

void mainWidget::paintEvent(QPaintEvent *event)
{
    QStylePainter painter(this);
    QStyleOption opt;
    opt.initFrom(this);
    opt.rect = this->rect();
    painter.drawPrimitive(QStyle::PE_Widget,opt);
    QWidget::paintEvent(event);
}

There are many irregular buttons on the main window (some buttons are combined into button groups, and the appearance of the button group changes together when a button is pressed, which is realized by setting the style sheet), each button has setMask to set the effective area, the button is pressed, and the button is released. On and selected will have different styles, set by setStyleSheet.

It turns out that the button is very slow in the response process. Top looks at the CPU usage and reaches 90%+ when the button is clicked. The redraw event will be called when the appearance of the control changes.

Guess : When the button is operated (pressed, released, clicked), the style sheet of the button is changed in its slot function, and the button is a sub-control of the main window. So it will also operate on the style sheet of the main window. That is, the redraw event of the main window is also called. As a result, every time a button is clicked, the redraw event of the main window must be called at least three times. Repeatedly calling the repaint event multiple times makes for slower response times in embedded platforms.

Solution

The main window does not use the style sheet method to set the background, but uses the palette method instead, and the main window style sheet is empty at this time:

mainWidget::mainWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::mainWidget)
{
    QPixmap pixmap;
    QPalette palette;
    setWindowFlags(Qt::FramelessWindowHint);
    pixmap.load(":/images/res/mianbackground.png");
    palette.setBrush(QPalette::Window, QBrush(pixmap));
    setPalette(palette);
    ui->setupUi(this);
···
}

At this time, when the buttons are operated, the CPU usage has been greatly reduced, and basically it will not exceed 10%.

6. Create a large number of controls in a short time

Phenomenon

If you create multiple controls in a short period of time, such as a 10x10 control grid, the test found that it will consume more CPU resources, especially if the custom control itself consumes more resources.

Solution

  • Optimize custom controls and reduce the resources required to create a single control;
  • After creating one or a part of the control, add some sleep() delay, but this will cause the creation of the control to be slower, and the visual effect is not very good. It depends on how to choose;
  • If you are creating a large number of controls that only need to be displayed, such as some icon controls, you can consider using Qt's Graphics View framework. Creating primitives is much faster than creating QWidegt controls, but you need to consider the nested use of QWidget and QGraphicsScene.

7. Calling qDebug() multiple times in a short period of time causes the print to freeze

Phenomenon

If an operation is performed, and then qDebug() is called multiple times in a short period of time to print, the test finds that it will also consume CPU resources.

Solution

So don't add too many prints at ordinary times, and delete some debug prints after the official release. If you don’t want to delete some prints for the convenience of testing, you can choose the official version to close the prints. During the test, you can use the terminal to execute ./proName d to run the application to output prints, and d is the suffix to output prints. code show as below:

// 默认先关闭qDebug()打印,以减少短时间内输出大量qDebug()打印造成的卡顿
QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, false);
// 如果执行./proName d,则开放打印
if(QString::fromUtf8(argv[1]) == "d") {
	printf("Open qDebug!!!!!\n");
	QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, true);
}

8. Set thread priority

Phenomenon
There are multi-threaded operations in the project, and all threads use the default QThread::InheritPriority priority. Due to the limited performance of the device, all threads are run with high priority, and the main thread is occupied too much resources, resulting in the problem of interface refresh lag.

Solution
Qt threads provide the following priorities (arranged from low to high), and setting the priority according to the actual situation can improve the fluency of the interface.

  • QThread::IdlePriority
  • QThread::LowestPriority
  • QThread::LowPriority
  • QThread::NormalPriority
  • QThread::HighPriority
  • QThread::HighestPriority
  • QThread::TimeCriticalPriority
  • QThread::InheritPriority

9. Check the delay operation of the child thread

Symptom
The code structure of a sub-thread in the project is like this.

void run() {
	while (true) {
		if (...) {
   			continue;
		}
	
		..... //operate
		msleep(20);
	}
}

The thread will hand over the CPU to other threads in the delayed state to avoid continuously preempting the CPU. Although the above code has a delay, there is a judgment operation in front. If the condition is met, continue is called directly. If this If the judgment condition is always satisfied, it will continue continuously, and the code will become the following form.

void run() {
	while (true) {
   		continue;
	}
}

It is equivalent to an infinite loop, which will greatly affect the efficiency of the program.

Workaround
Add a delay before each continue.

void run() {
	while (true) {
		if (...) {
			msleep(20);
   			continue;
		}
	
		..... //operate
		msleep(20);
	}
}

Or avoid using continue:

void run() {
	while (true) {
		if (...) {
			..... //operate
		}
	
		msleep(20);
	}
}

In addition, for multi-threading, sleep should be used reasonably. For time-consuming processing in while(1), just add Sleep(0); for non-time-consuming processing, adding Sleep(0) will not work, Sleep(1), Sleep(5) will do.

10. Optimize the database query method

If the amount of data in the table is large or the query frequency is high, you can use the map container to store it, and exchange space for time to reduce query loss. Or adjust the data structure, change the array of 200 or 300 pieces of data into a map structure for storage, etc.

11. Optimization of communication protocol

The amount of computation on the client and server can be reduced by optimizing the communication protocol.

12. Reduce unnecessary IO operations

IO operations are time-consuming. Common IOs include communication IO and file IO. You can check related codes for optimization.

The benefits of this article, free to receive Qt development learning materials package, technical video, including (C++ language foundation, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

Guess you like

Origin blog.csdn.net/QtCompany/article/details/131615629