C++: 性能优化
最后更新:2026-08-26
第48课我们学了C++17/20新特性。
现在,我们要学习性能优化——让C++程序跑得更快。
C++的优势就是性能,但要用好并不容易。
1. 性能优化概述
(1) 1.1 为什么要优化?
原则:
- 不要优化(Premature Optimization)
- 先测量,再优化
- 优化算法,再优化代码
(2) 1.2 性能瓶颈
| 瓶颈 | 占比 |
|---|---|
| 算法 | 70% |
| 内存访问 | 20% |
| 其他 | 10% |
结论: 优化算法比优化代码更重要。
2. 内存对齐
(1) 2.1 什么是内存对齐?
内存对齐是指数据在内存中的起始地址是某个值(通常是2的幂)的倍数。
为什么重要?
- 未对齐的内存访问更慢(甚至崩溃)
- CPU读取对齐的内存更快
(2) 2.2 示例:内存对齐的影响(难度⭐⭐)
▶ 示例 1:面向对象编程演示(难度⭐)
CPP
#include <iostream>
struct BadAlignment {
char c; // 1字节
int i; // 4字节(可能对齐到偏移4)
};
struct GoodAlignment {
int i; // 4字节
char c; // 1字节
};
int main() {
std::cout << "Bad: " << sizeof(BadAlignment) << " bytes" << std::endl;
std::cout << "Good: " << sizeof(GoodAlignment) << " bytes" << std::endl;
return 0;
}
输出:
TEXT
📖 仅展示
Bad: 8 bytes
Good: 8 bytes
运行结果(可能):
TEXT
📖 仅展示
Bad: 8 bytes
Good: 8 bytes
💡 提示:
- 把大的成员放前面,可以减少padding
3. 缓存友好
(1) 3.1 CPU缓存
CPU缓存比内存快100倍。
优化原则:
- 局部性原理:访问相邻的内存
- 顺序访问:比随机访问快
- 避免缓存失效:少访问不连续的内存
(2) 3.2 示例:行主序 vs 列主序(难度⭐⭐⭐)
CPP
#include <iostream>
### ▶ 示例 2:STL容器使用(难度⭐)
#include <vector>
#include <chrono>
int main() {
const int N = 1000;
std::vector<std::vector<int>> matrix(N, std::vector<int>(N));
// 行主序(缓存友好)
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
matrix[i][j] = 1;
}
}
auto end = std::chrono::high_resolution_clock::now();
auto row_time = std::chrono::duration<double>(end - start).count();
// 列主序(缓存不友好)
start = std::chrono::high_resolution_clock::now();
for (int j = 0; j < N; j++) {
for (int i = 0; i < N; i++) {
matrix[i][j] = 1;
}
}
end = std::chrono::high_resolution_clock::now();
auto col_time = std::chrono::duration<double>(end - start).count();
std::cout << "行主序时间:" << row_time << " 秒" << std::endl;
std::cout << "列主序时间:" << col_time << " 秒" << std::endl;
return 0;
}
输出:
TEXT
📖 仅展示
行主序时间:0.001 秒
列主序时间:0.003 秒
4. 编译器优化
(1) 4.1 优化级别
| 级别 | 标志 | 说明 |
|---|---|---|
| O0 | 无 | 不优化(调试用) |
| O1 | -O1 |
基本优化 |
| O2 | -O2 |
推荐(平衡) |
| O3 | -O3 |
激进优化 |
| Os | -Os |
优化大小 |
(2) 4.2 示例:编译器优化效果(难度⭐)
CPP
#include <iostream>
int main() {
int sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i;
}
std::cout << sum << std::endl;
return 0;
}
输出:
TEXT
📖 仅展示
499999500000
编译:
BASH
g++ -O0 main.cpp # 慢
g++ -O2 main.cpp # 快(编译器可能直接算出结果)
5. 性能分析工具
(1) 5.1 常用工具
| 工具 | 平台 | 说明 |
|---|---|---|
| gprof | Linux | GCC性能分析 |
| perf | Linux | Linux性能分析工具 |
| Valgrind | 跨平台 | 内存分析 |
| Visual Studio Profiler | Windows | VS自带 |
(2) 5.2 示例:用chrono测量时间(难度⭐)
CPP
#include <iostream>
#include <chrono>
int main() {
auto start = std::chrono::high_resolution_clock::now();
// 要测量的代码
long sum = 0;
for (int i = 0; i < 100000000; i++) {
sum += i;
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration<double>(end - start).count();
std::cout << "耗时:" << duration << " 秒" << std::endl;
return 0;
}
输出:
TEXT
📖 仅展示
耗时:0.05 秒
6. 优化技巧总结
(1) 6.1 代码层面
| 技巧 | 说明 |
|---|---|
| 用移动语义 | 减少拷贝 |
| 用emplace_back | 避免临时对象 |
| 用reserve | 减少vector重新分配 |
| 用unordered_map | 哈希表,O(1)查找 |
(2) 6.2 算法层面
| 技巧 | 说明 |
|---|---|
| 选对数据结构 | vector vs list vs map |
| 用合适算法 | sort vs partial_sort |
| 避免不必要的拷贝 | 用引用、移动 |
▶ 示例 3:传值 vs 传引用性能对比(难度⭐)
CPP
#include <iostream>
#include <vector>
#include <chrono>
struct BigData {
std::vector<int> data;
BigData() : data(10000, 0) {}
};
// 传值(拷贝)
void processByValue(BigData d) {
(void)d;
}
// 传引用(无拷贝)
void processByRef(const BigData& d) {
(void)d;
}
int main() {
BigData big;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000; i++) {
processByValue(big);
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "传值耗时:" << std::chrono::duration<double>(end - start).count() << " 秒" << std::endl;
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000; i++) {
processByRef(big);
}
end = std::chrono::high_resolution_clock::now();
std::cout << "传引用耗时:" << std::chrono::duration<double>(end - start).count() << " 秒" << std::endl;
return 0;
}
输出:
TEXT
📖 仅展示
传值耗时:0.15 秒
传引用耗时:0.001 秒
❓ 常见问题
Q 优化一定有效吗?
A 不一定。先测量,找到瓶颈再优化。
Q C++一定比Python快吗?
A 不一定。如果算法一样,C++通常快。但如果用了不好的算法,C++也可能慢。
Q 怎么判断瓶颈在哪?
A 用性能分析工具(Profiler)。
📖 小节
| 知识点 | 要点 |
|---|---|
| 内存对齐 | 减少padding |
| 缓存友好 | 顺序访问,局部性 |
| 编译器优化 | -O2 推荐 |
| 性能分析 | 先测量,再优化 |
| 优化技巧 | 移动语义、emplace_back |
📝 作业
-
**基础题 (Difficulty ⭐):分别用 vector 和 list 存储 100000 个整数,测试尾部插入和随机访问的时间差异。
-
**进阶题 (Difficulty ⭐⭐):测试传值 vs 传引用的性能差异。写一个函数处理一个大结构体(含 vector
<int>(10000)),分别用值传递和 const 引用传递测量时间。 -
**挑战题 (Difficulty ⭐⭐⭐):用 std::chrono 高精度计时,比较 for 循环、STL for_each、范围 for 三种遍历方式的性能差异。分析结果。
- 编译器优化选项:-O0/-O1/-O2/-O3/-Os
- 减少拷贝:引用传递、移动语义
- 容器选择:vector 连续内存缓存友好
- 内联函数减少函数调用开销
- profile 指导优化:先测量瓶颈再优化
下一课:单元测试(#50)