当前位置: 首页 > news >正文

GoogleBenchmark用法

目录

  • 介绍
  • 构建安装
    • 项目集成
    • 注意
  • 基本用法
    • 示例代码
    • 代码解析
    • 编译运行
  • 高级功能
    • 参数化测试
      • 控制时间测量
      • 多线程测试
      • 性能计数器
      • 自定义输出
    • 避免优化干扰
    • 自定义统计
    • 过滤测试
  • 实践
  • 常见问题
  • 参考资源

介绍

Google Benchmark 是一个功能强大的 C++ 微基准测试库,用于精确测量代码的性能。它由 Google 开发和维护,提供了丰富的功能来支持各种基准测试场景,从简单函数的性能测量到复杂的参数化测试。它通过自动调整迭代次数以确保统计稳定性,提供了高精度的性能数据。相比手动计时,Google Benchmark 具有以下优势:

  • 自动化迭代:动态确定运行次数以获得稳定的统计结果。
  • 多种时间测量:支持 CPU 时间、墙钟时间(Wall Clock Time)以及进程总 CPU 时间。
  • 灵活的参数化:支持参数化测试,方便测试不同输入规模或配置。
  • 多线程支持:能够处理多线程环境下的性能测量。
  • 输出格式多样:支持控制台、JSON 和 CSV 输出,便于分析和可视化。
  • 集成性强:与 Google Test 集成良好,易于嵌入现有项目。

构建安装

-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON:自动下载 Google Test 依赖。
-DCMAKE_BUILD_TYPE=Release:以 Release 模式构建,优化性能。
如果不需要测试,可以添加 -DBENCHMARK_ENABLE_GTEST_TESTS=OFF

项目集成

在自己的项目中,可以通过 CMake 集成 Google Benchmark:

  1. 使用find_package
find_package(benchmark REQUIRED)
target_link_libraries(your_target benchmark::benchmark benchmark::benchmark_main)
  1. 使用 add_subdirectory: 将 Google Benchmark 作为子目录添加到项目:
add_subdirectory(benchmark)
target_link_libraries(your_target benchmark::benchmark benchmark::benchmark_main)

注意

在 GCC 下,必须链接 pthread 库(-lpthread),否则可能出现运行时异常。
如果使用硬件性能计数器(如 libpfm),需要确保系统支持并配置 kernel.perf_event_paranoid(Linux 特有):

sudo sh -c 'echo kernel.perf_event_paranoid=-1 >> /etc/sysctl.conf'
sudo sysctl -p

基本用法

Google Benchmark 的核心是定义基准测试函数并通过宏注册到框架中。

示例代码

#include <benchmark/benchmark.h>// 定义基准测试函数
static void BM_StringCreation(benchmark::State& state) {for (auto _ : state) {std::string empty_string;}
}// 注册基准测试
BENCHMARK(BM_StringCreation);// 另一个基准测试
static void BM_StringCopy(benchmark::State& state) {std::string x = "hello";for (auto _ : state) {std::string copy(x);}
}
BENCHMARK(BM_StringCopy);// 程序入口
BENCHMARK_MAIN();

代码解析

  1. 基准测试函数
  • 必须接受一个 benchmark::State& 参数,用于控制测试状态。
  • 使用 for (auto _ : state) 循环,框架会自动控制迭代次数。
  • 测试代码放在循环体内,框架会测量其执行时间。
  1. 注册基准测试:使用 BENCHMARK 宏将函数注册为基准测试。
  2. 程序入口:BENCHMARK_MAIN() 定义主函数,初始化框架并运行所有注册的基准测试。

编译运行

输出示例(控制台格式):

------------------------------------------------------
Benchmark            Time             CPU   Iterations
------------------------------------------------------
BM_StringCreation   12 ns           12 ns   10000000
BM_StringCopy       25 ns           25 ns    5000000

输出解释:
Time:墙钟时间(Wall Clock Time),单位为纳秒(ns)。
CPU:主线程的 CPU 时间(默认)。
Iterations:测试运行的迭代次数,框架自动调整以确保结果稳定。

高级功能

Google Benchmark 提供了许多高级功能,支持更复杂的测试场景。

参数化测试

参数化测试允许测试不同输入规模或配置。以下是几种参数传递方式:

  1. 使用 Arg,为基准测试指定单个参数值:
static void BM_Memcpy(benchmark::State& state) {char* src = new char[state.range(0)];char* dst = new char[state.range(0)];memset(src, 'x', state.range(0));for (auto _ : state) {memcpy(dst, src, state.range(0));}state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));delete[] src;delete[] dst;
}
BENCHMARK(BM_Memcpy)->Arg(8)->Arg(64)->Arg(512)->Arg(1024);
  1. 使用 Range
  • 指定参数范围,框架自动生成参数(默认以 8 倍递增):
BENCHMARK(BM_Memcpy)->Range(8, 8<<10); // 从 8 到 8192
  • 自定义递增倍数:
BENCHMARK(BM_Memcpy)->RangeMultiplier(2)->Range(8, 8<<10); // 以 2 倍递增
  1. 使用 Args,支持多参数测试:
static void BM_SetInsert(benchmark::State& state) {std::set<int> data;for (auto _ : state) {state.PauseTiming(); // 暂停计时以排除初始化影响data = ConstructRandomSet(state.range(0));state.ResumeTiming();for (int j = 0; j < state.range(1); ++j) {data.insert(RandomNumber());}}
}
BENCHMARK(BM_SetInsert)->Args({1<<10, 1})->Args({1<<10, 8})->Args({8<<10, 1});
  1. 使用 Ranges,指定多参数范围,测试所有组合:
BENCHMARK(BM_SetInsert)->Ranges({{1<<10, 8<<10}, {1, 512}});
  1. 自定义参数,使用 Apply 自定义参数组合:
static void CustomArguments(benchmark::internal::Benchmark* b) {for (int i = 0; i <= 10; ++i) {for (int j = 0; j <= 50; ++j) {b->Args({i, j});}}
}
BENCHMARK(BM_SetInsert)->Apply(CustomArguments);

控制时间测量

Google Benchmark 默认测量主线程的 CPU 时间,但支持以下选项:

  • 墙钟时间:使用 UseRealTime() 测量实际时间:
BENCHMARK(BM_test)->UseRealTime();
  • 进程总 CPU 时间:测量所有线程的 CPU 时间:
BENCHMARK(BM_test)->MeasureProcessCPUTime();

多线程测试

  1. 支持多线程基准测试:
static void BM_MultiThread(benchmark::State& state) {for (auto _ : state) {// 多线程代码}
}
BENCHMARK(BM_MultiThread)->Threads(4); // 使用 4 个线程
  1. 自定义线程模型(例如使用 OpenMP):
static void BM_OpenMP(benchmark::State& state) {for (auto _ : state) {#pragma omp parallel forfor (int i = 0; i < state.range(0); i++) {// 工作负载}}
}
BENCHMARK(BM_OpenMP)->Range(8, 8<<10);

性能计数器

在 Linux 上,可以使用 libpfm 收集硬件性能计数器(如 CPU 周期、指令数):

BENCHMARK(BM_test)->UseRealTime()->PerfCounters({"CYCLES", "INSTRUCTIONS"});

需要确保 libpfm 已安装,并配置 CMake 时启用 -DBENCHMARK_ENABLE_LIBPFM=ON。

自定义输出

支持多种输出格式:

  1. 控制台:默认,可读,带颜色。
  2. JSON:
./benchmark_example --benchmark_format=json
  1. CSV:
./benchmark_example --benchmark_format=csv

设置输出文件:

./benchmark_example --benchmark_out=output.json --benchmark_out_format=json

避免优化干扰

编译器优化可能导致代码被优化掉,影响基准测试结果。使用以下方法防止优化:

  1. benchmark::DoNotOptimize:防止编译器优化掉结果:
for (auto _ : state) {int result = ComputeSomething();benchmark::DoNotOptimize(result);
}
  1. benchmark::ClobberMemory:确保内存写入生效:
for (auto _ : state) {ComputeAndStore();benchmark::ClobberMemory();
}

自定义统计

设置处理的数据量或项目数:

state.SetBytesProcessed(int64_t(state.iterations()) * state.range(0));
state.SetItemsProcessed(state.iterations());

过滤测试

  1. 使用 --benchmark_filter 运行特定测试:
./benchmark_example --benchmark_filter=BM_StringCreation
  1. 正则表达式支持:
./benchmark_example --benchmark_filter=BM_String.*

实践

  1. 优化级别:
  • 始终以 Release 模式(-O3)编译基准测试,避免 Debug 模式导致不准确的结果。
  • 检查编译器优化是否移除关键代码,使用 DoNotOptimize 或 ClobberMemory。
  1. 统计稳定性:Google Benchmark 自动调整迭代次数以确保结果稳定,但仍需多次运行以验证一致性。
  2. 多线程测试:
  • 多线程测试可能受系统调度影响,确保测试环境稳定(如固定 CPU 核心)。
  • 使用 taskset(Linux)控制 CPU 核心分配。
  1. 避免副作用:确保基准测试代码不依赖全局状态或外部资源(如文件、网络),否则可能引入噪声。
  2. 分析结果:
  • 使用 JSON 输出结合脚本(如 Python 的 compare.py)分析性能差异。
  • 关注 Time 和 CPU 的差异,可能反映 I/O 或系统调度的影响。
  1. 硬件性能计数器:在 Linux 上使用 libpfm 获取更多性能指标(如缓存命中率),但需要管理员权限。

常见问题

  1. 如何避免编译器优化干扰?

使用 benchmark::DoNotOptimize 和 benchmark::ClobberMemory。例如:

int result = Compute();
benchmark::DoNotOptimize(result);
benchmark::ClobberMemory();
  1. 如何比较两个版本的性能?

运行两个版本的基准测试,保存 JSON 输出,然后使用 compare.py:

./benchmark_old --benchmark_out=old.json --benchmark_out_format=json
./benchmark_new --benchmark_out=new.json --benchmark_out_format=jsonpython3 compare.py old.json new.json
  1. 如何测试 GPU 代码?

使用手动计时:

static void BM_GPU(benchmark::State& state) {for (auto _ : state) {state.PauseTiming();// 准备 GPU 数据state.ResumeTiming();// 执行 GPU 计算}
}
BENCHMARK(BM_GPU)->UseManualTime();
  1. 如何在 CI 中集成?

使用 Bencher 等工具,结合 JSON 输出,在 CI 管道中运行并分析结果:

bencher run --adapter cpp_google "./benchmark_example --benchmark_format=json"

参考资源

  1. 官方文档:https://github.com/google/benchmark/blob/main/docs/user_guide.md
  2. GitHub 仓库:https://github.com/google/benchmark
  3. 讨论组:https://groups.google.com/g/benchmark-discuss
  4. 性能计数器:https://github.com/google/benchmark/blob/main/docs/perf_counters.md
  5. 博客教程:https://www.bencher.dev/learn/benchmarking/cpp/google-benchmark/
http://www.dtcms.com/a/288266.html

相关文章:

  • 环形区域拉普拉斯方程傅里叶级数解
  • 电阻耐压参数学习总结
  • 再谈进程-控制
  • 敏感词 v0.27.0 新特性之词库独立拆分
  • 5-大语言模型—理论基础:注意力机制优化
  • 关于个人博客系统的测试报告
  • Typecho评论系统集成Markdown编辑器完整教程
  • Windows事件查看器完整指南
  • 最少标记点问题:贪心算法解析
  • 深入了解 find_element 方法:Web 自动化定位元素的核心​
  • Linux某个进程CPU占用率高原因定位手段
  • Vue基础(前端教程①-路由)
  • 从 C# 转 Python 第三天:文件操作、异常处理与错误日志实践
  • 量子计算与AI融合的技术突破与实践路径
  • 物联网系统中-告警配置功能的定义
  • #Datawhale组队学习#7月-强化学习Task2
  • Java行为型模式---状态模式
  • python类Keys
  • kombu 运行超长时间任务导致RabbitMQ消费者断开
  • 智能制造——解读39页汽车行业数字化工厂解决方案【附全文阅读】
  • 【RK3576】【Android14】调试方法
  • JavaSE-接口
  • Buildroot vs Yocto:SDK 构建机制的核心差异与实践案例
  • Python爬虫开发实战:Selenium自动化与浏览器控制全解析
  • YOLOv11改进 | DWRSeg扩张式残差助力小目标检测
  • web前端渡一大师课 02 浏览器渲染原理
  • Zara和网易云音乐仿写总结
  • Cortex-M内核的屏障指令
  • 并行编程实战——CUDA入门编程的函数
  • 亚马逊 TM 标产品反跟卖实战:从平台规则到技术工具的立体防御体系