i007.cc

i007.cc

优先队列-降维打击

05.价值资料

C++ 性能测量工具全景


🔷 一、按场景分类

微观测量(单行代码/函数级)  →  Chrono 计时、Google Benchmark
宏观性能分析(找瓶颈)        →  perf、gprof、Valgrind/Callgrind
内存问题排查                  →  Valgrind/Memcheck、AddressSanitizer
可视化分析                    →  perf + FlameGraph、VTune

 


🔷 二、最基础:std::chrono 手动计时

cpp
#include <chrono>
#include <iostream>

void heavyFunction() {
    std::vector<int> v(1000000);
    std::iota(v.begin(), v.end(), 0);
    std::sort(v.rbegin(), v.rend());
}

int main() {
    auto start = std::chrono::high_resolution_clock::now();
    
    heavyFunction();
    
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    std::cout << "耗时: " << duration.count() << " 微秒" << std::endl;
}

 

⚠️ 常见陷阱:编译器优化把代码”优化没了”

cpp
auto start = std::chrono::high_resolution_clock::now();
int result = expensiveComputation();
auto end = std::chrono::high_resolution_clock::now();
// ❌ 如果 result 没被用到,编译器可能直接把整个计算删掉!

 

解决:用 volatile 或确保结果被使用:

cpp
volatile int result = expensiveComputation();  // 阻止优化掉
// 或者
std::cout << result;  // 确保编译器不能优化掉计算

 


🔷 三、专业基准测试:Google Benchmark

chrono 手动计时不可靠(只跑一次,受系统抖动影响很大)。生产级做法是用专门的基准测试库,自动多次运行、统计、消除噪声。

cpp
#include <benchmark/benchmark.h>

static void BM_VectorPushBack(benchmark::State& state) {
    for (auto _ : state) {        // 自动循环多次,直到统计稳定
        std::vector<int> v;
        for (int i = 0; i < 1000; i++) {
            v.push_back(i);
        }
        benchmark::DoNotOptimize(v);  // 防止编译器优化掉
    }
}
BENCHMARK(BM_VectorPushBack);

static void BM_VectorReserve(benchmark::State& state) {
    for (auto _ : state) {
        std::vector<int> v;
        v.reserve(1000);   // 对比:预分配 vs 不预分配
        for (int i = 0; i < 1000; i++) {
            v.push_back(i);
        }
        benchmark::DoNotOptimize(v);
    }
}
BENCHMARK(BM_VectorReserve);

BENCHMARK_MAIN();

 

输出示例:

Benchmark                  Time             CPU   Iterations
BM_VectorPushBack       3245 ns         3240 ns       215000
BM_VectorReserve        1820 ns         1818 ns       384000

 

一眼看出reserve() 预分配让性能提升近一倍——这正是基准测试的价值:用数据说话,而不是猜

进阶:参数化测试不同输入规模

cpp
static void BM_Sort(benchmark::State& state) {
    int n = state.range(0);   // 测试不同规模
    std::vector<int> v(n);
    for (auto _ : state) {
        std::iota(v.begin(), v.end(), 0);
        std::sort(v.rbegin(), v.rend());
    }
}
BENCHMARK(BM_Sort)->Arg(1000)->Arg(10000)->Arg(100000);
// 一次性看到不同规模下的性能曲线,判断算法复杂度是否符合预期

 


🔷 四、系统级性能剖析:perf(Linux)

chrono/Benchmark 告诉你”“,但不知道”为什么慢、慢在哪一行“。perf 能精确定位热点函数。

bash
# 编译时保留调试信息(关键!否则看不到函数名/行号)
g++ -O2 -g myprogram.cpp -o myprogram

# 采样运行时的性能数据
perf record -g ./myprogram

# 查看热点报告
perf report

 

输出类似:

Overhead  Symbol
  45.2%   sortAlgorithm()      ← 45%的时间花在这里!
  22.1%   memoryAllocation()
  12.3%   std::vector::push_back

 

火焰图可视化(更直观)

bash
git clone https://github.com/brendangregg/FlameGraph

perf record -g ./myprogram
perf script | ./FlameGraph/stackcollapse-perf.pl | ./FlameGraph/flamegraph.pl > flame.svg

 

火焰图中,横向宽度 = 耗时占比,一眼看出哪个函数调用链最耗时。


🔷 五、内存问题排查:Valgrind

Memcheck:检测内存泄漏、越界访问

bash
g++ -g myprogram.cpp -o myprogram
valgrind --leak-check=full ./myprogram

 

输出:

==12345== HEAP SUMMARY:
==12345==     in use at exit: 40 bytes in 1 blocks
==12345== LEAK SUMMARY:
==12345==    definitely lost: 40 bytes in 1 blocks
==12345==    at 0x... malloc
==12345==    by 0x... main (myprogram.cpp:15)   ← 精确定位泄漏行号!

 

Callgrind:函数调用次数 + 耗时分析

bash
valgrind --tool=callgrind ./myprogram
kcachegrind callgrind.out.12345   # 图形界面查看调用图

 

缺点:Valgrind 会让程序慢 10-50 倍(模拟执行环境),不适合实时性能测试,只适合”找问题”。


🔷 六、更轻量的内存检测:AddressSanitizer (ASan)

比 Valgrind 快得多(只慢 2 倍左右),编译时启用:

bash
g++ -g -fsanitize=address myprogram.cpp -o myprogram
./myprogram

 

cpp
int main() {
    int* p = new int[10];
    p[10] = 5;  // 越界写入
    delete[] p;
}

 

运行时直接报错并定位:

==12345==ERROR: AddressSanitizer: heap-buffer-overflow
    #0 main (myprogram.cpp:3)   ← 精确指出第3行越界

 

还有配套的:

  • -fsanitize=threadTSan):检测数据竞争(多线程 bug 神器)
  • -fsanitize=undefinedUBSan):检测未定义行为(整数溢出等)
bash
# 检测多线程数据竞争
g++ -g -fsanitize=thread myprogram.cpp -o myprogram

 


🔷 七、CPU 缓存/指令级分析:Intel VTune(更专业级)

适合极致优化场景(高频交易、游戏引擎),能分析:

  • CPU 缓存命中率(L1/L2/L3 miss)
  • 分支预测失败率
  • SIMD 向量化是否生效
bash
vtune -collect hotspots ./myprogram

 

图形界面展示具体到汇编指令级别的耗时分布,适合”压榨最后5%性能”的场景。


🔷 工具选择速查表

目标 工具
快速看某段代码耗时 std::chrono
严谨对比不同实现的性能 Google Benchmark
找整个程序的性能瓶颈 perf + 火焰图
内存泄漏 / 越界 Valgrind / AddressSanitizer
多线程数据竞争 ThreadSanitizer
极致底层优化(缓存/指令) Intel VTune

🔷 实战流程建议

1. 先用 std::chrono 粗略定位"哪个功能慢"
        ↓
2. 用 perf + 火焰图 找到具体"哪个函数/哪行代码"是瓶颈
        ↓
3. 针对瓶颈写 Google Benchmark,量化对比优化前后效果
        ↓
4. 用 ASan/TSan 确保优化没引入内存/并发 bug

 


一句话总结

chrono 用于”知道慢不慢”,perf/火焰图用于”知道慢在哪”,Google Benchmark 用于”证明优化有没有用”,Valgrind/Sanitizer 用于”确保优化没搞出新bug” —— 四者配合,才是完整的性能优化闭环。

想深入哪个方向?比如 cache-friendly 数据结构设计(如何写出 CPU 缓存友好的代码)或者 SIMD 向量化优化

发表回复