3.5. 编译器优化
3.5.1. 优化级别
3.5.1.1. GCC/Clang 优化选项
-O0 # 无优化(默认),最快编译
-O1 # 基本优化
-O2 # 推荐的优化级别
-O3 # 激进优化,可能增加代码大小
-Os # 优化代码大小
-Ofast # -O3 + 不严格遵循标准的优化
3.5.1.2. 常用编译选项
# 推荐的生产环境编译选项
g++ -O2 -march=native -flto -DNDEBUG program.cpp
# 调试时保留调试信息
g++ -O2 -g program.cpp
# 性能分析
g++ -O2 -g -fno-omit-frame-pointer program.cpp
3.5.2. 链接时优化 (LTO)
# GCC
g++ -O2 -flto program.cpp -o program
# Clang (ThinLTO - 更快的增量编译)
clang++ -O2 -flto=thin program.cpp -o program
LTO 允许编译器跨编译单元优化,包括:
跨文件内联
死代码消除
全局优化
3.5.3. 查看编译器优化
3.5.3.1. 生成汇编代码
# 生成汇编
g++ -O2 -S program.cpp
# 带源码注释
g++ -O2 -S -fverbose-asm program.cpp
# 使用 Compiler Explorer (godbolt.org)
3.5.3.2. 编译器报告
# GCC 优化报告
g++ -O2 -fopt-info-vec -fopt-info-inline program.cpp
# Clang 优化报告
clang++ -O2 -Rpass=inline -Rpass-missed=inline program.cpp
3.5.4. 帮助编译器优化
3.5.4.1. restrict 指针
// 告诉编译器指针不会别名
void add_arrays(float* __restrict a,
float* __restrict b,
float* __restrict result,
int n) {
for (int i = 0; i < n; ++i) {
result[i] = a[i] + b[i];
}
}
3.5.4.2. 循环优化提示
// OpenMP SIMD
#pragma omp simd
for (int i = 0; i < n; ++i) {
result[i] = a[i] + b[i];
}
// GCC 循环展开
#pragma GCC unroll 4
for (int i = 0; i < n; ++i) {
process(i);
}
// 告诉编译器循环至少执行多少次
void process(int* data, int n) {
__builtin_assume(n >= 1000);
for (int i = 0; i < n; ++i) {
// 编译器可以更积极优化
}
}
3.5.4.3. [[likely]] 和 [[unlikely]] (C++20)
if (value > 0) [[likely]] {
process(value);
} else [[unlikely]] {
handle_error();
}
// 或者 GCC 内置函数
if (__builtin_expect(value > 0, 1)) {
process(value);
}
3.5.4.4. 内联提示
// 建议内联
inline int fast_function(int x) { return x * 2; }
// 强制内联 (GCC/Clang)
[[gnu::always_inline]] inline int very_fast(int x) { return x * 2; }
// 禁止内联
[[gnu::noinline]] void debug_function() { }
3.5.5. 目标架构优化
# 为当前 CPU 优化
g++ -O2 -march=native program.cpp
# 指定架构
g++ -O2 -march=skylake program.cpp
# 启用 AVX2
g++ -O2 -mavx2 program.cpp
# 查看支持的架构
gcc --target-help
3.5.6. Profile-Guided Optimization (PGO)
# 第一步:生成插桩程序
g++ -O2 -fprofile-generate program.cpp -o program
# 第二步:运行程序收集数据
./program # 使用典型工作负载
# 第三步:使用收集的数据重新编译
g++ -O2 -fprofile-use program.cpp -o program_optimized
3.5.7. 常见优化
3.5.7.1. 死代码消除
void example() {
int x = 10;
int y = 20;
int z = x + y; // 如果 z 从未使用,会被消除
if (false) {
// 这段代码会被消除
}
}
3.5.7.2. 常量传播
int calculate() {
int a = 5;
int b = 10;
return a * b; // 编译器直接计算为 50
}
3.5.7.3. 循环不变量外提
// 优化前
for (int i = 0; i < n; ++i) {
result[i] = data[i] * expensive_constant(); // 每次调用
}
// 优化后(编译器自动)
auto c = expensive_constant();
for (int i = 0; i < n; ++i) {
result[i] = data[i] * c;
}
3.5.7.4. 循环展开
// 优化前
for (int i = 0; i < 4; ++i) {
data[i] = i;
}
// 优化后
data[0] = 0;
data[1] = 1;
data[2] = 2;
data[3] = 3;
3.5.7.5. 向量化
// 编译器可能将此向量化
void add(float* a, float* b, float* c, int n) {
for (int i = 0; i < n; ++i) {
c[i] = a[i] + b[i];
}
}
// 查看向量化报告
// g++ -O2 -fopt-info-vec program.cpp
3.5.8. 编译时计算
// constexpr 在编译时计算
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
// 编译时已知结果
constexpr int result = factorial(10); // 3628800
// C++20 consteval 强制编译时计算
consteval int must_be_compile_time(int n) {
return n * 2;
}
备注
编译器优化要点:
使用 -O2 作为基准优化级别
考虑 LTO 进行跨模块优化
为特定架构使用 -march=native
用 PGO 获得更好的分支预测
使用 Compiler Explorer 验证优化