当前位置: 首页 > news >正文

C++性能优化常用技巧

一. 选择合适的数据结构

1.1 map与unordered_map的选择

如果仅仅只需要使用到快速查找的特性,那么unordered_map更加合适,他的复杂度是O(1)。如果还需要排序以及范围查找的能力,那么就选择map。

1.2 vector与list的选择

通常情况下,顺寻存储一些数据,然后通过下标进行查找,就选择vector。那么什么时候选择list呢?常见的一个用法就是在LRU 缓存的实现中,我们会使用list来存放缓存结点,而不是vector,主要原因就在于list的插入和删除是高效的。

1.3 unordered_map与自定义查找表的选择

如果不需要复杂的哈希函数,仅通过数组下标的形式就能完成哈希,比如key是26个字母或者连续的数字。此时只需要定义一个数组就能实现哈希存储(自定义查找表)。

二. 选择合适的算法

算法对性能的影响是值得考虑的,以排序算法举例:

  • 快速排序:适合数据比较均匀的场景,如果数据已经有序或者接近有序,此时快速排序会退化到O(n*n)的复杂度。
  • 堆排序:适合求topK问题。
  • 归并排序:O(n)的空间复杂度,但是排序性能不会因为数据本身已经有序而退化。
  • 冒泡排序:一般情况下不推荐此排序。性能较差。

三. 避免不必要的拷贝

3.1 使用引用

引用可以直接与被引用对象共享同一份存储,可以认为引用是给对象起一个别名。如下示例代码:

// 形参使用引用
void show(const std::string& str)
{// do something
}std::vector<std::string> strs;
/*
对strs进行填充
*/
// 使用引用接收返回值,前提是函数返回对象的引用
const std::string& str = strs[1];
3.2 使用移动语义

移动语义一般用在初始化对象或者给对象赋值时,可以避免对象的拷贝。如下代码:

std::string stra = "abc";
// 拷贝stra对象
std::string strb = stra;
// 移动strb的资源到strc中
std::string strc = std::move(strb);
3.3 返回值优化(RVO)

通常编译器都有RVO优化的功能,它允许直接在调用者的栈帧上构造对象,从而避免了额外的拷贝和资源析构的开销。如下代码:

class A {
public:A() {}~A() {}A(const A& rhs) {}A(A&& rhs) {}
};int func()
{A a;return a;
}int main()
{A a = func();return 0;
}

上述代码中,返回值优化之后,类A的构造函数和析构函数只被调用一次,不会产生临时变量的构造与析构,以及拷贝构造的过程。

四. 合适的内存管理

4.1 智能指针

在管理动态内存时,智能指针可以帮助我们实现更可靠的内存管理,避免内存泄漏等严重问题。C++11中提供了unique_ptr和shared_ptr两个智能指针,那么该如何选择呢?

  • shared_ptr:共享智能指针。一个对象可能在多个地方被共享。
  • unique_ptr:独占智能指针。一个对象只能被一个智能指针对象所拥有。

通常情况下,如果能明确是独占的场景,那么就选择unique_ptr,虽然shared_ptr也能保证正确性,但是后者性能要比前者差30%。因为uniqe_ptr更接近裸指针,而shared_ptr内部实现相对复杂(引用计数、控制块等)。

4.2 内存池

内存池是一个预先申请和分配好的内存区域。在需要申请内存资源时,可以直接在内存池中获取,在释放内存时,将内存返还给内存池。从而避免了频繁的内存分配和释放的过程。google的tcmalloc就提供了这样的功能。

4.3 对象池

对象池同样也是一种预先申请和分配内存的技术,区别于内存池的是,其针对的是特定的类对象的内存管理。如果一个类型需要频繁的进行对象的创建和释放,并且对象的创建比较耗时。那么我们可以选择使用池化技术,预先创建好一定数量的对象,放到对象池中。其实很多池化技术都属于这个范畴,只是针对不同场景,有其独特的叫法,如MySQL的连接池、线程池等等。

4.4 栈内存的管理

通常我们只需要管理堆内存,而栈内存交给操作系统来管理。但是栈内存的申请和初始化依旧是由程序员来控制的,而操作系统只负责内存的分配和释放。考虑如下场景:

void func()
{for (int i = 0; i < 999999; ++i) {char data[1000000];// do something}
}

上述data在每一次循环都需要分配一次内存,然后对其进行操作。如果data的创建可以放到循环体之外进行也不影响程序的正确性。那么data的内存分配只需要一次即可。
事实上,我们还可以继续优化。因为这块内存很大,函数也可能会被频繁调用,每次调用都会重复申请一大块内存。所以考虑创建一个静态的data变量,或者静态的全局变量。只要它能满足程序的正确性要求。何乐而不为呢?当然静态变量在多线程环境下会存在数据竞争的问题。此时还可以考虑使用threadlocal变量

五、减少函数调用

5.1 使用内联函数

一次函数调用涉及两次指令跳转。如果一个函数频繁调用,可以使用内联机制来避免函数的调用。C++11提供了inline关键字,来告诉编译器被修饰的函数可以在调用处进行替换。一般这样的函数是一些功能简单,代码行较少的函数。当然是否内联取决于编译器,inline只是给编译器建议。是否内联可通过查看汇编代码来确认。

5.2 减少函数递归调用

函数递归通常写起来比较方便,并且也更好理解。但是函数递归带来的开销是巨大的,随着递归深度的加深,性能也会受到影响。稍有不慎,甚至会造成栈溢出。所以应该尽量避免使用递归函数,考虑使用迭代或者只使用尾递归(编译器优化,只需要当前的栈帧空间,无需开辟新空间)的方式。

六、多线程处理

多线程可以利用多核特性,同时处理多个任务,从而提高程序性能。多线程常常涉及数据竞争的问题,为了提高多线程的性能,应减少数据竞争,常见的方法有:

  • 使用原子变量。
  • 使用读写锁。
  • 降低锁的持有时间。
  • 使用线程池模型,重复利用线程资源,利用多队列减少工作线程间的数据竞争。
  • 使用无锁数据结构,避免频繁的线程上下文切换。
  • 减少需要共享的状态。

七、编译器优化

在考虑性能问题之前,首先得开启编译器优化,很多时候,代码虽然写的不是最优的,但是在开启编译优化之后,往往能达到很好的优化效果。常见的优化选项:

  • O0:不做任何优化。
  • O1:主要对代码的分支,常量以及表达式等进行优化。
  • O2:会尝试更多的寄存器级的优化以及指令级的优化。
  • O3:在O2的基础上进行更多的函数内联优化,因此目标代码会更大,但执行速度会更快。

八、指令级优化

8.1 SIMD指令

SIMD(单指令多数据)指的是具有多个处理元件的计算机同时对多个数据执行相同操作的过程。以加法为例,通常情况下,我们需要先取操作数1,再取操作数2,然后求和。而SIMD指令则支持同时取多个操作数,然后执行求和运算。也就是说取操作数的过程是并行的。在gcc中可以通过添加ftree-vectorize编译选项,或者开启O2优化,就可以让编译器使用SIMD优化代码。

九、提高缓存命中率

CPU有3级缓存L1、L2、L3,其中L1离核心最近,因此速度也最快。由于缓存有大小限制,因此只有少量的数据和指令会被加载到缓存中,所以经常会出现缓存无法命中的问题。因此提高cache命中率就可以提高程序性能。

9.1 选择合适的数据结构

数据结构多使用连续的存储,如vector,而少使用list、map这种非连续存储类型。因为连续的内存通常会被一起加载到缓存中。

9.2 内存对齐

内存不对齐的情况下,cpu可能需要夸多个内存行去获取数据。从而增加了cpu的访存次数,也增加了缓存不命中的可能性。

9.3 减少条件分支

程序中的条件判断,如 if-else 语句,可以通过逻辑重组或使用分支预测优化来提高缓存命中率。在某些情况下,消除不必要的条件判断或将其重构为更高效的形式可以减少缓存行的加载和卸载,从而提高性能。例如,使用gcc的提供的关键字__builtin_expect来告诉编译器哪个分支命中的概率最高,从而实现优化。

9.4 其他
  • 避免频繁的内存分配与释放,减少内存碎片。
  • 循环展开,减少循环次数。
  • 循环中按行处理数据要比按列处理更高效。

相关文章:

C++性能优化常用技巧

一. 选择合适的数据结构 1.1 map与unordered_map的选择 如果仅仅只需要使用到快速查找的特性&#xff0c;那么unordered_map更加合适&#xff0c;他的复杂度是O(1)。如果还需要排序以及范围查找的能力&#xff0c;那么就选择map。 1.2 vector与list的选择 通常情况下&#…...

IntelliJ IDEA集成MarsCode AI

IntelliJ IDEA集成MarsCode AI IDEA中安装插件 安装完毕之后登录自己的账号 点击链接&#xff0c;注册账号 https://www.marscode.cn/events/s/i5DRGqqo/ 可以选择不同的模型...

数据挖掘工程师的技术图谱和学习路径

数据挖掘工程师的技术图谱和学习路径: 1.基础知识 数据挖掘工程师是负责从大量数据中发现潜在模式、趋势和规律的专业人士。以下是数据挖掘工程师需要掌握的基础知识: 数据库知识:熟悉关系数据库和非关系数据库的基本概念和操作,掌握SQL语言。 统计学基础:了解统计学的基…...

Excel基础(详细篇):总结易忽视的知识点,有用的细节操作

目录 基础篇Excel主要功能必会快捷键LotusExcel的文件类型工作表基本操作表项操作选中与缩放边框线 自动添加边框线格式刷设置斜线表头双/多斜线表头不变形的:双/多斜线表头插入多行、多列单元格/行列的移动冻结窗口 方便查看数据打印的常见问题Excel格式数字格式日期格式文本…...

基因枷锁下的太空梦 —— 千钧一发电影观后感

目录 1 人物介绍 2 电影名解读 3 电影开头 3.1 电影开头的两段话 3.2 片头设计 4 电影正文 4.1 “杰罗米”各种诡异的行为 4.2 文森特 – 失败的man 4.3 真正的杰罗米以及假基因身份证 4.4 文森特新征程 4.5 基因人的不容易 4.6 睫毛被查出有问题 4.7 文森特身份初…...

leetcode第40题组合总和Ⅱ

原题出于leetcode第40题https://leetcode.cn/problems/combination-sum-ii/题目如下&#xff1a; 给定一个候选人编号的集合 candidates &#xff08;candidate中有重复的元素&#xff09;和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合…...

迷你世界脚本状态接口:Buff

状态接口&#xff1a;Buff 迷你世界 更新时间: 2023-04-26 20:07:54 具体函数名及描述如下: 序号 函数名 函数描述 1 addBuff(...) 给对象附加效果 2 removeBuff(...) 给对象移除指定效果 3 clearAllBuff(...) 给对象清除所有效果 4 clearAllBadBu…...

Java中Stream流的详细使用介绍

Java中Stream流的详细使用介绍 **1. 创建 Stream**从集合创建从数组创建使用 Stream.of 创建创建无限流 **2. 中间操作**过滤&#xff1a;filter映射&#xff1a;map去重&#xff1a;distinct排序&#xff1a;sorted截取&#xff1a;limit 和 skip **3. 终端操作**收集&#xf…...

【重构小程序】升级JDK1.8、SpringBoot2.x 到JDK17、Springboot 3.x(一)

前言 最近想着把大火的deepseek 迁移到小程序里&#xff0c;基于刷题小程序的数据库做一个RAG应用&#xff0c;来进一步扩展答案解析&#xff0c;帮助用户解答相关问题。但是由于之前做的项目都要老了&#xff0c;并不支持spring 的AI模块&#xff0c;因此&#xff0c;我打算先…...

功能丰富的自动化任务软件zTasker_2.1.0_绿色版_屏蔽强制更新闪退

&#x1f680; zTasker 一键式效率倍增器使用指南 &#x1f64f; 致谢 首先感谢开发者提供如此高效的工具&#xff01; 软件本身功能强大&#xff0c;但部分机制需特别注意&#xff01; &#x1f4d6; 软件概述 zTasker 是一款通过自动化脚本/任务流实现效率飞跃的生产力工…...

_ 为什么在python中可以当变量名

在 Python 中&#xff0c;_&#xff08;下划线&#xff09;是一个有效的变量名&#xff0c;这主要源于 Python 的命名规则和一些特殊的使用场景。以下是为什么 _ 可以作为变量名的原因和常见用途&#xff1a; --- ### 1. **Python 的命名规则** Python 允许使用字母&#xff…...

Java 9 到 Java 21 新特性全解析:从语法简化到API增强

一、新特性的概述 纵观Java这几年的版本变化&#xff0c;在Java被收入Oracle之后&#xff0c;Java以小步快跑的迭代方式&#xff0c;在功能更新上迈出了更加轻快的步伐。基于时间发布的版本&#xff0c;可以让Java研发团队及时获得开发人员的反馈&#xff0c;因此可以看到最近…...

LeeCode题库第三十九题

39.组合总和 项目场景&#xff1a; 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同…...

卫星网络仿真平台:IPLOOK赋能空天地一体化通信新生态​

卫星仿真平台 在6G技术加速演进与天地一体化网络建设的大背景下&#xff0c;卫星通信作为地面网络的重要补充&#xff0c;正成为全球通信覆盖的关键支撑。IPLOOK凭借其深厚的技术积累与创新实践&#xff0c;推出的卫星网络仿真平台​&#xff08;SCEPS&#xff09;&#xff0c…...

(十一)基于vue3+mapbox-GL实现模拟高德实时导航轨迹播放

要在 Vue 3 项目中结合 Mapbox GL 实现类似高德地图的实时导航轨迹功能,您可以按照以下步骤进行: 安装依赖: 首先,安装 mapbox-gl 和 @turf/turf 这两个必要的库: npm install mapbox-gl @turf/turf引入 Mapbox GL: 在组件中引入 mapbox-gl 并初始化地图实例: <templ…...

计算机面试项目经历描述技巧

在计算机类岗位的面试中&#xff0c;项目经历是面试官评估候选人技术能力、问题解决能力和实战经验的核心环节。以下是专业化的项目经历描述策略&#xff08;附模板框架&#xff09;&#xff1a; 一、结构化表达框架&#xff08;STAR-RT模型&#xff09; Situation&#xff08;…...

132. 分割回文串 II

简单分析 输入的参数是字符串s&#xff0c;返回值是最小的切割次数。那这个问题的典型解法应该是动态规划&#xff0c;因为我们需要找最优解&#xff0c;而每一步的选择可能会影响后面的结果&#xff0c;但可以通过子问题的最优解来构建整体最优解。 那么动态规划的状态如何定…...

【每日学点HarmonyOS Next知识】全局调整字体、h5选择框无法取消选中、margin不生效、Length转换为具体值、Prop和link比较

【每日学点HarmnoyOS Next知识】全局调整字体、h5选择框无法取消选中、margin不生效、Length转换为具体值、Prop和link比较 1、HarmonyOS 是否存在统一调整全局字体大小的方法&#xff1f; 是否存在统一调整全局字体大小的方法 可以用动态属性&#xff0c;自定义class实现At…...

九、Spring Boot:自动配置原理

深入解析 Spring Boot 自动配置原理 Spring Boot 的自动配置机制是其最核心的特性之一&#xff0c;它极大地简化了 Spring 应用的初始搭建和开发过程。通过自动配置&#xff0c;Spring Boot 能够根据项目的依赖和配置自动加载和配置 Spring 应用的各个部分。本文将深入探讨 Sp…...

(动态规划 最长重复子数组)leetcode 718

思路就是建立一个二维的dp数组&#xff0c;只要nums1[i]nums2[j]&#xff08;nums1和nums2出现重复元素就置1 并加上左上角的值) 为什么代码是nums1 i-1和nums2 i-1 答&#xff1a;因为i和j以1为初始值开始遍历的 为什么要这么做并且为什么要加dp【i-1】【j-1】&#xff1f; …...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...