C++如何进行性能优化?
一、C++如何进行性能优化?
1.1 回答重点
一般性能优化都是具体问题具体分析,但也可以总结出一些相对通用的小技巧。
1.1.1 算法和数据结构的选择
选择适合的算法和数据结构,比如,在频繁查找的场景中使用哈希表而不是链表,可以显著提升性能。
1.1.2 减少不必要的对象创建和销毁
没有必要的临时对象,能避免就避免。对象的创建和销毁会触发构造函数和析构函数的调用,这些操作在频繁执行时会带来不小的开销。
1.1.3 内存管理
了解和使用C++的内存分配与释放机制,可以帮助避免内存泄漏和碎片化。尽量使用智能指针(如std::shared_ptr、std:unique_ptr)来管理内存,避免手动new和delete操作。
1.1.4 内联函数
对于短小且频繁调用的函数,可以使用inline关键字,将函数定义为内联函数,减少函数调用的开销。
1.1.5 避免不必要的拷贝
充分利用C++11提供的移动语义(movesemantics),减少对象拷贝所带来的开销。使用std:move来进行对象移动,而不是复制。
1.1.6 多线程和并行计算
对于计算密集型任务,可以考虑使用多线程和并行计算,利用多核CPU提升性能。C++11引入的标准库std::thread、std::future等使用起来都很方便。
1.1.7 代码缓存友好性
尽量让数据按访问顺序在内存中排列,减少缓存未命中率cache miss,特别是在处理大量数据时,排序后的数组会比链表性能更好。
1.1.8 预编译与链接优化
利用编译器的优化选项(如GCC的-O3选项)以及链接器的优化(如链接时消除未用函数)来提升代码性能。
1.1.9 预分配内存
比如标准化容器中的reserve,需要频繁创建内存的地方可以考虑预分配一块内存出来,避免频繁的创建内存。
1.1.10 尽可能多的使用缓存
将某些数据保存下来供下次使用,避免再次获取或重新计算它们。
1.2 扩展知识
1.2.1算法和数据结构的选择
- ·如果你有频繁插入和删除操作,std:list(链表)可能更适合:如果需要快速索引,那么std:vector(动态数组)是很好的选择。
- ·对于大规模数据的排序,选择合适的排序算法,如快速排序、归并排序等,能显著提升性能。
1.2.2 对象创建和销毁
·避免在循环里反复创建和销毁对象。可以考虑对象池来重复利用对象。
1.2.3 内存管理
·使用RAII机制,在对象的构造时获取资源,在析构时释放资源。合理使用智能指针。
1.2.4 内联函数
一般来说,内联函数适用于小函数。过度使用内联会导致代码增大,加载时间增多。要平衡使用。
1.2.5 移动语义
C++11中,引入了移动构造函数和移动赋值运算符。这些特性可以让数据在容器间移动而不是拷贝,大大提高效率。
1.2.6 多线程和并行计算
·使用C++标准库提供的std:thread来创建线程,可以使用std:async、std:future来处理异步任务。为避免数据争用,可以使用std:mutex、std:lock_guard等进行同步控制。
1.2.7 代码缓存友好性
·数据的局部性(Temporal andSpatial Locality),即在较短时间内访问的内存地址尽可能是相邻的,可以优化缓存利用率。例如可以将常用数据和频繁调用的代码放在一起。
1.2.8 预编译与链接优化
- ·使用GCC或Clang时,可以通过添加编译标志-o2或03来启用高级别的优化。
- ·链接优化可使用 -flto (Link Time Optimization)标志,这样编译器可以在链接时进行跨模块优化。
1.2.9 预分配内存
比如标准化容器中的reserve,需要频繁创建内存的地方可以考虑预分配一块内存出来,避免频繁的创建内存。也要尽可能的重用内存,特别是一个线程内的内存,能重用的内存尽量重复使用,这样也可以避免频繁创建内存。
1.2.10 尽可能多的使用缓存
如果任务或计算特别慢,应该保证不执行那些没有必要的任务或者重复计算。
网络通信:如果频繁发起相同的网络请求,可考虑将第一次的网络请求结果保存在内存中,或文件中?
- ·磁盘访问:如果频繁访问一个文件,可考虑将这个文件的内 容保存在内存中。
- ·数学计算:某些很耗时很复杂的运算,可考虑只执行这种计 算一次,然后共享结果。
- ·对象分配:如果需要大量频繁创建和销毁短期对象,可考虑 使用对象池。
- ·线程创建:如果需要大量频繁创建和销毁线程,可考虑使用 线程池。
1.2.11 其他:
-
·选择合适的数据结构:选择合适的STL,想清楚什么时候用栈,什么时候用队列,什么时候用数组,什么时候用链表。
-
·某些if-else可改为switch,效率可能更高(知道为什么吗,不知道的可以留言,人多的话考虑输出一篇文章)。
-
·优先考虑栈内存,而不是堆内存(免得频繁的申请释放内存)。 ·如何函数不需要返回值,就不要设置返回值。
-
·使用位操作,移位代替乘法除法操作。 ·构造函数时使用初始化方式,而不是赋值。
-
·明确使用模板带来的益处:如果使用模板并没有给你的开发带来任何益处,是不是可以考虑不使用它,因为调试起来真的麻烦。
-
·函数参数的个数不要太多。 ·擅用emplace,有些情况下会省去一次构造的开销。