Qt 性能优化:CPU占有率高的现象和解决办法
一、前言
在最近的项目中,发现执行 Qt 程序时,有些情况下的 CPU 占用率奇高,最高高达 100%。项目跑在嵌入式板子上,最开始使用 EGLFS 插件,但是由于板子没有单独的鼠标层,导致鼠标移动起来卡顿,很不流畅,所以换成了 LinuxFB 插件。但是如果 CPU 占有率高了的话,也会导致鼠标卡顿,因为鼠标是由 Qt 应用程序层负责绘制的,如果应用程序 CPU 占有率高了的话,也会导致鼠标绘制缓慢,导致鼠标卡顿。
所以要解决 CPU 占有率高的问题,进行性能优化,本文对项目中容易出现的问题现象与解决办法进行了记录。
二、UI 控件频繁刷新
现象
代码逻辑控制的 UI 控件(包括 QWidget)频繁刷新,或者某个动作频繁执行。UI 刷新在一定程度上会对CPU占用率有影响,若频繁刷新控件(不论是重绘还是更新数据),都会十分消耗 CPU 占用率。例如一个时间 Label,采用定时器设置时间,如果定时器的定时间隔过短,比如 10ms 甚至更短,就会比较消耗 CPU 占用率。
解决方法
所以自定义 UI 控件时就需要注意避免频繁刷新;定时器的间隔时间也要合理设置,避免过短或过长。
三、paintEvent 高频绘图
这个情况很常见,但相关内容很多不方便放在这,可以看我的下一篇博客:Qt 性能优化之二:绘制视频方案选择
四、优化界面刷新
现象
项目中有一项功能是在子线程里读数据,数据若发生变化了就发送信号,在槽里更新界面。程序运行的时候会出现数据变化很频繁的情况,也导致更新界面的信号在短时间内被触发很多次,大大损耗了程序的性能。
解决方法
因为人眼捕获的帧率有限,所以界面显示不需要太快的刷新速度,把信号槽更新界面改为用定时器更新界面,就能避免某一时间段数据变化过频导致的卡顿问题。
五、样式表导致绘图事件频繁调用
现象
QWidget:主窗口,背景利用setStyleSheet设置,同时重写paintEvent事件(QWigdet 的 paintEvent 默认为空):
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);
}
主窗口上有众多不规则按键(某些按键组合成按键组,按下一个按键按键组的外观一起改变,通过设置样式表实现),每个按键都setMask设置有效区域,按键在按下,松开,选中都会有不同的样式,通过setStyleSheet设置。
结果发现,按键在响应过程中非常慢,top 看一下 CPU 占用率在点击按键时达到了 90%+,重绘事件在控件外观发生变化时会被调用。
猜测:在操作按键(按下,松开,点击)时在其槽函数改变按键的样式表,按键为主窗口的子控件。所以也会操作到主窗口的样式表。也即,主窗口的重绘事件也被调用。也就导致了每点击一个按键,最少也要调用三次主窗口的重绘事件。重复多次调用重绘事件使得在嵌入式平台中响应速度变慢。
解决方法
主窗口不使用样式表方式设置背景,改用调色板方式,此时主窗口样式表为空:
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);
···
}
此时操作按键,CPU 占用率已经大大减小了,基本不会有超过 10% 的情况。
六、短时间内创建大量控件
现象
如果短时间内创建多个控件,例如一个 10x10 的控件网格,测试发现也会比较消耗 CPU 资源,尤其这个自定义控件本身比较消耗资源的话。
解决方法
- 优化自定义控件,减少单个控件创建所需的资源;
- 每创建一个或者一部分控件后,加点 sleep() 延时,但是这样会导致创建控件比较慢,视觉效果也不是很好,看怎么取舍了;
- 如果是创建大量只需要显示的控件,例如一些图标控件,可以考虑使用 Qt 的图形视图 Graphics View 框架,创建图元比创建 QWidegt 控件快多了,但是需要考虑 QWidget 与 QGraphicsScene 的嵌套使用。
七、短时间内多次调用 qDebug() 打印造成卡顿
现象
如果执行了一个操作,然后短时间内多次调用 qDebug() 打印,测试发现也会比较消耗 CPU 资源。
解决方法
所以平时不要加太多打印,正式发布后一些调试打印要即使删除。如果为了测试方便,有些打印不想删除,可以选择正式版本关闭打印,测试时可以使用终端执行./proName d来运行应用程序以输出打印,d是要输出打印的后缀。代码如下:
// 默认先关闭qDebug()打印,以减少短时间内输出大量qDebug()打印造成的卡顿
QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, false);
// 如果执行./proName d,则开放打印
if(QString::fromUtf8(argv[1]) == "d") {printf("Open qDebug!!!!!\n");QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, true);
}
八、设置线程优先级
现象
项目中有多线程的操作,所有的线程都是用默认的QThread::InheritPriority优先级。因为设备的性能有限,让所有的线程都以高优先级去运行,主线程被过多地占用资源,导致界面刷新卡顿的问题。
解决方法
Qt 的线程提供了以下几种优先级(由低到高排列),按照实际情况去设置优先级,可以提高界面的流畅度。
- QThread::IdlePriority
- QThread::LowestPriority
- QThread::LowPriority
- QThread::NormalPriority
- QThread::HighPriority
- QThread::HighestPriority
- QThread::TimeCriticalPriority
- QThread::InheritPriority
九、检查子线程的延时操作
现象
项目里有一个子线程的代码结构是这样的。
void run() {while (true) {if (...) {continue;}..... //operatemsleep(20);}
}
线程在延时状态下是会把 CPU 交给其它线程的,避免不停地抢占 CPU,以上代码虽然加了延时,但前面有一个判断操作,如果满足了条件,就直接调用continue,如果这个判断条件一直都是满足的,那就会不停地 continue,代码也就变成了以下形式。
void run() {while (true) {continue;}
}
等同于一个死循环,这样会大大影响程序的效率。
解决方法
在每个 continue 前添加延时。
void run() {while (true) {if (...) {msleep(20);continue;}..... //operatemsleep(20);}
}
或者避免使用 continue:
void run() {while (true) {if (...) {..... //operate}msleep(20);}
}
另外多线程,要合理的使用 sleep。对于while(1)中有耗时处理的,添加Sleep(0)就可以;对于没有耗时处理的,添加Sleep(0)就不行了,Sleep(1),Sleep(5)都行。
十、优化数据库查询方式
如果表中数据量大或者查询频率高,可以使用 map 容器来存放,以空间换时间,减少查询损耗。或者调整数据结构,将 2,3 百条数据的数组改成 map 结构储存等等。
十一、通信协议的优化
可以通过优化通信协议来减少客户端和服务端的计算量。
十二、减少不必要的IO操作
IO 操作是比较耗时的,常见的 IO 有通信 IO 和文件 IO,可以检查相关代码进行优化。
相关文章:
Qt 性能优化:CPU占有率高的现象和解决办法
一、前言 在最近的项目中,发现执行 Qt 程序时,有些情况下的 CPU 占用率奇高,最高高达 100%。项目跑在嵌入式板子上,最开始使用 EGLFS 插件,但是由于板子没有单独的鼠标层,导致鼠标移动起来卡顿,…...
MySQL专题(学会就毕业)
MySQL专题0.准备sql设计一张员工信息表,要求如下:编号(纯数字)员工工号 (字符串类型,长度不超过10位)员工姓名(字符串类型,长度不超过10位)性别(男/女,存储一…...
Java高级技术:单元测试、反射、注解
目录 单元测试 单元测试概述 单元测试快速入门 单元测试常用注解 反射 反射概述 反射获取类对象 反射获取构造器对象 反射获取成员变量对象 反射获取方法对象 反射的作用-绕过编译阶段为集合添加数据 反射的作用-通用框架的底层原理 注解 注解概述 自定义注解 …...
C语言初识
#include <stdio.h>//这种写法是过时的写法 void main() {}//int是整型的意思 //main前面的int表示main函数调用后返回一个整型值 int main() {return 0; }int main() { //主函数--程序的入口--main函数有且仅有一个//在这里完成任务//在屏幕伤输出hello world//函数-pri…...
Cadence Allegro 导出Etch Length by Layer Report报告详解
⏪《上一篇》 🏡《上级目录》 ⏩《下一篇》 目录 1,概述2,Etch Length by Layer Report作用3,Etch Length by Layer Report示例4,Etch Length by Layer Report导出方法4.2,方法14.2,方法2B站关注“硬小二”浏览更多演示视频...
无监督对比学习(CL)最新必读经典论文整理分享
对比自监督学习技术是一种很有前途的方法,它通过学习对使两种事物相似或不同的东西进行编码来构建表示。Contrastive learning有很多文章介绍,区别于生成式的自监督方法,如AutoEncoder通过重建输入信号获取中间表示,Contrastive M…...
最长回文子串【Java实现】
题目描述 现有一个字符串s,求s的最长回文子串的长度 输入描述 一个字符串s,仅由小写字母组成,长度不超过100 输出描述 输出一个整数,表示最长回文子串的长度 样例 输入 lozjujzve输出 // 最长公共子串为zjujz,长度为…...
LeetCode 438. Find All Anagrams in a String
LeetCode 438. Find All Anagrams in a String 题目描述 Given two strings s and p, return an array of all the start indices of p’s anagrams in s. You may return the answer in any order. An Anagram is a word or phrase formed by rearranging the letters of a…...
MyBatis-1:基础概念+环境配置
什么是MyBatis?MyBatis是一款优秀的持久层框架,支持自定义sql,存储过程以及高级映射。MyBatis就是可以让我们更加简单的实现程序和数据库之间进行交互的一个工具。可以让我们更加简单的操作和读取数据库的内容。MyBatis的官网:htt…...
R语言基础(五):流程控制语句
R语言基础(一):注释、变量 R语言基础(二):常用函数 R语言基础(三):运算 R语言基础(四):数据类型 6.流程控制语句 和大多数编程语言一样,R语言支持选择结构和循环结构。 6.1 选择语句 选择语句是当条件满足的时候才执行…...
【Java开发】设计模式 02:工厂模式
1 工厂模式介绍工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使…...
合并两个链表(自定义位置合并与有序合并)LeetCode--OJ题详解
图片: csdn 自定义位置合并 问题: 给两个链表 list1 和 list2 ,它们包含的元素分别为 n 个和 m 个。 请你将 list1 中 下标从 a 到 b 的全部节点都删除,并将list2 接在被删除节点 的位置。 比如: 输入:list1 [1…...
Java编程问题总结
Java编程问题总结 整理自 https://github.com/giantray/stackoverflow-java-top-qa 基础语法 将InputStream转换为String apache commons-io String content IOUtils.toString(new FileInputStream(file), StandardCharsets.UTF_8); //String value FileUtils.readFileT…...
binutils工具集——objcopy的用法
以下内容源于网络资源的学习与整理,如有侵权请告知删除。 一、工具简介 objcopy主要用来转换目标文件的格式。 在实际开发中,我们会用该工具进行格式转换与内容删除。 (1)在链接完成后,将elf格式的.out文件转化为bi…...
Windows使用Stable Diffusion时遇到的各种问题和知识点整理(更新中...)
Stable Diffusion安装完成后,在使用过程中会出现卡死、文件不存在等问题,在本文中将把遇到的问题陆续记录下来,有兴趣的朋友可以参考。 如果要了解如何安装sd,则参考本文《Windows安装Stable Diffusion WebUI及问题解决记录》。如…...
MySQL workbench基本查询语句
1.查询所有字段所有记录 SELECT * FROM world.city; select 表示查询;“*” 称为通配符,也称为“标配符”。表示将表中所有的字段都查询出来;from 表示从哪里查询;world.city 表示名为world的数据库中的city表; 上面…...
软件测试详解
文章目录一、软件危机(一)概念(二)产生软件危机的原因(三)消除软件危机的途径二、软件过程模型(一)软件生命周期概念(二)软件开发模型1. 瀑布模型2. 螺旋模型…...
YOLOS学习记录
在前面,博主已经完成了YOLOS项目的部署与调试任务,并在博主自己构造的数据集上进行了实验,实验结果表明效果并不显著,其实这一点并不意外,反而是在情理之中。众所周知,Transformer一直以来作为NLP领域的带头…...
数组边遍历(for循环)边删除为什么删不干净 及三种实现删除的方法
文章目录1、为什么删不干净倒序删迭代器lambda表达式删除为什么说数组边for循环遍历边删除会出现删不干净的情况1、为什么删不干净 先写一个例子:可以先猜一下控制台会打印出什么内容? public class removeIterator {public static void main(String[]…...
环境配置之Keepass
前言很久以前,就有了想要一个自己密码管理器的念头。毕竟,即使浏览器能记住各个网站的账号密码,但是在登录单独客户端的时候,仍然要翻找密码。为了省事,也曾经是一个密码走天下。然后被劫持了QQ给同学发黄色小网站&…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
