https://notegpt.io/youtube-video-summarizer?utm_source=chatgpt.com
https://youtu.be/qCjEN5XRzHc?si=w4zxY2zf0Q1dMiF1

00:00:01
演讲介绍与目标
- 演讲者是一名自学的C++开发者,专长于实时渲染和游戏开发,拥有教育性YouTube频道,并已在OSD 822发表相关论文。
- 本次演讲旨在整合和系统展示多种C++性能优化技术,帮助听众了解哪些优化最有效,应如何选择。
- 强调基于性能测试来判断优化效果,介绍多种可能的优化方案及其优缺点。
00:01:04
高效程序的五大标准
- 避免不必要的工作:不执行冗余计算,重点关注避免不必要的复制和内存分配。
- 充分利用计算资源:利用多核CPU和SIMD指令集提升性能(不涉及GPU和其他加速器)。
- 避免不必要的等待和阻塞:减少线程间依赖和等待时间,采用无锁数据结构、非阻塞异步API或作业系统。
- 高效利用硬件特性:编写缓存友好、可预测的代码,利用复杂的推测执行和多级缓存机制。
- 操作系统层面的高效管理:通过操作系统特定API请求更多硬件资源,减少线程迁移和上下文切换。
三种实现路径
- 有效使用语言特性:利用C++语法和属性,如noexcept、constexpr等。
- 调整构建流程:更换编译器版本、标准库实现、编译器标志。
- 手动优化设计与实现:针对性能进行专门设计和编码调整。
00:05:21
构建流程优化举例
- 启用编译器优化标志(如-O2、-O3等),并设置目标架构以利用特定微架构优化。
- Fast Math选项:牺牲精度换取浮点运算速度,适用于对速度要求极高且能容忍精度损失的场景。
- 禁用异常和RTTI:减少开销,但需谨慎考虑语言完整性和异常处理需求。
- 链接时优化(LTO):允许链接器跨翻译单元优化,如内联函数调用,提升整体性能。
- Unity Build:将多个源文件合并为单个大翻译单元,提升编译器优化能力及编译速度,尤其适用于新项目。
- 静态链接依赖库:便于调用处优化,但会增大可执行文件体积,影响指令缓存利用。
- Profile Guided Optimization (PGO):通过运行时收集真实数据指导优化,生成更适合实际运行路径的代码。
- 尝试不同编译器和标准库:GCC、Clang、MSVC等产出不同性能结果,升级编译器版本和替换库实现可能带来性能提升。
- 二进制后处理工具(如LLVM BOLT):通过重排代码布局基于运行时分析进一步提升性能。
00:12:20
C++语言特性和注解的高效使用
- constexpr
- 指定表达式在编译时求值,减少运行时计算。
- 包括constexpr函数、变量和条件语句(if constexpr)。
- C++23新增
consteval(仅允许编译时求值)和constinit(变量必须编译时初始化,但可变)。
- const
- 标记不可变变量和成员函数,有助于编译器优化(如代码提升)。
- 自C++23支持显式对象参数声明,提高const正确性。
- noexcept
- 指明函数不会抛出异常,减少异常处理开销。
- 特别重要于移动构造函数和移动赋值操作符。
- static
- 标记内部链接符号,限制函数或变量只在当前翻译单元可见,便于编译器内联。
- static比inline更能帮助内联决策。
- 属性(attributes)
[[noreturn]]:函数永不返回,便于优化异常处理和代码流。[[likely]]和[[unlikely]]:提示分支预测,优化指令缓存和分支预测器。[[assume]](C++23标准化):假设某条件为真,帮助编译器消除冗余检查。
- restrict指针 (C++的C99扩展)
- 表明指针不与其它指针别名,允许更激进的优化,如向量化。
- pure/const函数属性 (GCC扩展)
- 标记无副作用函数,允许编译器做代码提升和重复调用合并。
00:27:03
函数参数传递和复制优化原则
- 参数传递方式取决于其类型和用途:
- 可空参数用
std::optional更安全,避免空指针。 - 需要所有权转移用智能指针,避免不必要的复制。
- 需要修改对象用左值引用,纯读取用常量引用或值传递(小型可复制对象)。
- 对连续内存数据用
std::span,非连续用范围或迭代器对。 - 字符串参数优先
std::string_view避免隐式转换和分配。 - 函数对象用模板参数或函数指针,C++23的
std::move_only_function提供更低开销。
- 可空参数用
- 避免循环内频繁堆分配,预先
reserve容器空间。 - 捕获异常应尽可能用引用,避免复制和异常切片。
- 使用值限定成员函数(value-qualified member functions)避免不必要复制,C++23的显式对象参数声明可简化重载。
00:36:14
现代硬件运行原理对性能优化的启示
- 虚拟内存机制
- 程序内存地址为虚拟地址,实际物理内存分散,页大小通常为4KB。
- 页表管理虚拟到物理内存映射。
- 当内存不足时,页面可能被换出至硬盘,引发性能大幅下降(抖动)。
- 性能关键程序应访问局部且连续的内存(工作集)。
- 缓存架构
- 处理器内置多级缓存(L1指令/数据缓存、L2、L3缓存),显著缩短访问延迟。
- 硬件预取器基于空间局部性预加载数据(缓存行64字节)。
- 时间局部性假设程序近期访问的数据很快会被再次访问。
- 存储结构应设计为缓存友好,如使用连续容器(数组、vector、flat结构),避免指针跳跃(链表、树)。
- 优化成员变量顺序,频繁访问的放前面,减少缓存未命中。
- 减少线程间缓存争用(False Sharing)
- 不同线程访问相邻缓存行内不同数据会导致性能下降。
- 应按
std::hardware_destructive_interference_size(通常64字节)隔离数据。
- 线程亲和性与优先级
- 固定线程绑定核心,减少缓存失效和线程迁移。
- 提升线程或进程优先级减少上下文切换,但需谨慎避免系统卡顿。
00:46:27
分支预测与分支优化
- 现代CPU大量并行和乱序执行,依赖分支预测器预判执行路径。
- 避免间接调用(虚函数、函数指针等)提升预测准确率。
- 设计代码时尽可能使分支可预测,减少分支跳转开销。
- 使用
[[likely]]和[[unlikely]]提示分支热度,帮助编译器优化。 - 分支消除(Branchless)技术用算术运算替代条件跳转,牺牲执行路径选择换取流水线效率,类似GPU的执行模型。
- 优化时需要进行性能剖析,判断分支优化是否真正带来收益。
00:50:45
SIMD向量化示例
- 现代CPU含专门的向量处理单元(AVX512等),能并行处理多数据。
- 利用SIMD内置函数(intrinsics)手写向量化代码,显著提升数据密集型运算性能。
- 大多数现代编译器(GCC等)能自动将普通C++代码向量化,但手动优化可进一步提升。
- 演示4x4矩阵乘法的SIMD内在实现,性能远超标量代码。
00:53:53
总结与建议
- 编译时应启用适合具体项目的优化选项,并使用C++特性注解代码以辅助编译器优化。
- 减少不必要复制和分配,设计合理参数传递和数据结构。
- 关注硬件架构,编写缓存友好、分支预测友好的代码。
- 手动或自动利用SIMD指令提升计算性能。
- 结合实时性能分析工具(如Clang-Tidy)和持续集成流程,自动检测和防止性能回退。
00:55:50
现场问答摘要
- Unity Build是否为一线选项?
是,尤其适合新项目,现有构建系统(CMake、MSBuild)均支持。 - Fast Math是否危险?
是,牺牲精度,需谨慎使用和测试,适合对速度优先的场景(如游戏)。 - no return的优化价值?
允许编译器省略返回相关异常处理和栈展开代码,优化冷路径。 - 参数为可空且复制昂贵时如何处理?
标准库无现成"非拥有optional视图",可用std::optional<std::reference_wrapper<T>>解决。 - 是否应普遍使用likely/unlikely?
不建议普遍使用;在无法使用PGO时对热点代码可用;PGO更能准确指导分支预测优化。 - 为何在某些极端场景下将不常用分支标为likely?
这是为了让编译器将该路径作为热代码优化,比如低频但极端重要的交易请求路径,以最大化响应速度。
关键词
- 性能优化、编译器优化、constexpr、noexcept、static、属性(attributes)、LTO、Unity Build、PGO、缓存友好、分支预测、SIMD向量化、内存布局、虚拟内存、线程亲和性、false sharing
结论

本次演讲全面涵盖了C++性能优化的多个层面,从构建流程、语言特性、代码设计到硬件架构,提供了实用且科学的优化思路,强调了性能测试和工具辅助的重要性。通过理解硬件原理结合现代C++特性,开发者能有效提升程序执行效率,并避免常见性能陷阱。