文章

C++性能优化

C++性能优化

C++ 性能优化的核心原则可以总结为:减少不必要的计算、提升内存访问效率、发挥硬件并行能力,以及利用编译期优化。

  1. 内存与缓存优化(现代 CPU 性能的关键)

提升数据局部性(Data Locality): 优先使用连续内存存储的容器(如 std::vector、std::array),减少对节点型容器(如 std::list、std::map)的使用,避免 CPU Cache Miss。

自定义内存分配器: 对于频繁创建/销毁的小对象,标准库的 malloc 或 new 会带来较大的开销与内存碎片。使用内存池(Memory Pool)或对象池能大幅提升分配效率。

优化结构体对齐(Memory Alignment): 调整结构体成员的排列顺序,按字节大小降序排列,减少补齐(Padding)字节;在多线程场景下要注意防止伪共享(False Sharing),通过 alignas(64) 将频繁修改的变量隔离在不同的缓存行(Cache Line)中。

  1. 语法与语言特性层面 减少不必要的拷贝(零拷贝与移动语义):

使用移动语义(Move Semantics):合理实现/利用移动构造和移动赋值,避免深拷贝。

值传递与引用传递: 复杂对象优先选择 const T& 传递;只读字符串使用 std::string_view(C++17),数组切片使用 std::span(C++20)。

原地构造: 使用 emplace_back 替换 push_back,避免临时对象的创建与拷贝。

利用编译期计算:

尽可能使用 constexpr / consteval 将计算从运行期提前到编译期完成。

减少虚函数与非必要动态绑定:

虚函数调用存在指针寻址开销,且阻碍编译器进行内联优化。对不需要被继承的类或虚函数标记 final,方便编译器进行去虚化(Devirtualization)。

采用 CRTP(Curiously Recurring Template Pattern,奇异递归模板模式) 实现静态多态。

  1. 算法与数据结构选择 选对数据结构: 查找密集型场景下,无序哈希表 std::unordered_map 通常比基于红黑树的 std::map 更高效;但数据量较小时,甚至连续数组 std::vector 加线性查找的速度都可能超过哈希表(因为 Cache 更友好)。

预分配内存容量: 对于 std::vector、std::string 等可扩容容器,在已知元素数量或大致范围时,提前调用 .reserve() 避免多次重新分配内存和搬运数据。

  1. 编译器优化选项与 PGO 许多性能提升只需开启编译器的优化开关:

基础优化等级: 发布版本务必开启 -O2 或 -O3(Clang/GCC)或 /O2(MSVC)。

PGO(Profile-Guided Optimization,基于性能分析的优化):

带 Instrumentation 标志编译程序,跑一遍典型业务流程收集 Profile 数据。

编译器利用这些数据重新编译(优化热点代码分支预测、函数内联与代码布局),通常能带来 5%~20% 的额外提升。

LTO / LTCG(Link-Time Optimization,链接期优化): 开启 -flto,打破跨编译单元(.cpp 文件)的壁垒,实现跨文件的函数内联和无用代码消除。

针对架构指令集优化: 指定 -march=native,允许编译器使用当前 CPU 支持的高级指令集(如 AVX2、AVX-512)。

  1. 多线程与并发优化 无锁(Lock-Free)编程: 在高并发读写热点上,尝试用 std::atomic 替换互斥锁 std::mutex;注意选择合适的内存顺序(Memory Order,如 std::memory_order_relaxed),避免过强同步锁死流水线。

线程池化: 避免频繁创建和销毁线程,采用任务队列 + 线程池模式复用工作线程。

cyclone 性能下降问题怎么比较优化结果 https://confluence.ext.net.nokia.com/pages/viewpage.action?pageId=1857328842

https://confluence.ext.net.nokia.com/pages/viewpage.action?spaceKey=5GRD&title=Cyclone

PGO 优化

Cyclone provides a method to monitor performance KPIs during test execution. KPIs like #instructions, #cycles consumed per slot and per scheduler phase (TD - FD - PostTd) can help identify bottlenecks or performance degradation in continuous integration.

Additionally more KPIs are tracked for snowfish (ABIO) that leverage Intel’s capability of TOPDOWN performance analysis. e.g; FE_ICACHE or FE_PREDECODE. TOPDOWN approach details here: Intel+Top-Down+Analysis+for+Processor+Performance

Monitoring is achieved by pushing these measured KPIs to a Grafana dashboard. Variations in L2-PS performance can be then easily tracked and fixed.

U-Plane SW Performance Aspects

3.1. TSC - Time Stamp Counter Counts the number of CPU cycles.

3.2. INS - Instructions Retired Event How many instructions were completely executed.

算法复杂度,单独开一期来写

任务切换 同一进程,共享地址空间,页表,代码段,只需要切寄存器和栈。 不同进程,除了寄存器,还要换页表,虚拟地址空间,TLB 进程切换 > 线程切换 > 函数调用 切换为导致 Cache失效, 性能下降

让同一个线程长期跑在同一个核上。Cache一直热着,性能会好很多。 通信软件常做的就是 线程绑定CPU核。

上下文切换本质上就是“给当前线程拍快照保存,再把另一个线程的快照恢复回来”。真正昂贵的不只是保存寄存器,而是切换后原来缓存中的代码和数据失效,导致CPU需要重新把新线程的工作集装入Cache,这部分成本往往远高于寄存器保存本身。

扩展阅读

[1] Optimized C++ by Kurt Guntheroth(O’Reilly). Copyright 2016 Kurt Guntheroth, 978-1-491-92206-4

本文由作者按照 CC BY 4.0 进行授权