Google Benchmark使用疑问及多线程存储引擎基准测试优化咨询
Google Benchmark 问题解答与代码优化
让我来逐个解决你的问题,结合Google Benchmark的最佳实践给出具体方案:
基础基准测试问题
1. 让初始化代码仅执行一次
你看到std::cout << "Foo"执行7次,是因为Google Benchmark会多次调用基准测试函数(BM_foo)来预热、调整迭代次数,每次调用都会执行函数开头的代码。要实现单次初始化,推荐使用线程安全的单次执行机制:
#include <benchmark/benchmark.h> #include <iostream> #include <mutex> static void BM_foo(benchmark::State& state) { // 用std::once_flag保证代码仅执行一次,多线程环境下也安全 static std::once_flag init_flag; std::call_once(init_flag, [](){ std::cout << "Foo " << std::endl; // 仅执行一次的初始化代码 }); for (auto _: state) { // 核心基准测试逻辑 } } BENCHMARK(BM_foo); BENCHMARK_MAIN();
2. 获取循环的连续序号
state.iterations()返回的是当前基准测试运行的总迭代次数,而非当前循环的序号。要得到1、2、3...的连续计数,需要自己在函数内维护计数器:
static void BM_foo(benchmark::State& state) { int current_iteration = 0; for (auto _: state) { current_iteration++; std::cout << current_iteration << " In loop " << std::endl; } }
存储引擎多线程基准测试问题
1. 是否必须使用全局变量DBConnection?
完全不需要!全局变量会带来耦合性和线程安全隐患,推荐用函数内静态变量+线程安全初始化来替代,既封装又安全:
// 无需全局变量,conn仅在当前基准测试函数内可见 static std::shared_ptr<DBConnection> conn; static std::once_flag conn_init_flag;
2. 避免openSession在conn初始化前执行,且不计入基准时间
需要结合线程同步和计时控制来解决,确保所有线程等待conn初始化完成,同时精准排除初始化/清理的计时:
#include <benchmark/benchmark.h> #include <mutex> #include <memory> static void BM_db_insert(benchmark::State& state) { // 初始化共享连接:仅线程0执行,其他线程等待 static std::shared_ptr<DBConnection> conn; static std::once_flag conn_init_flag; state.PauseTiming(); // 暂停计时,排除初始化耗时 if (state.thread_index() == 0) { std::call_once(conn_init_flag, [](){ conn = std::make_shared<DBConnection>(openDBConn()); }); state.WaitForAllThreads(); // 等待所有线程同步,确保conn已初始化 } else { state.WaitForAllThreads(); // 其他线程等待线程0完成初始化 } // 创建线程专属会话(不计入基准时间) std::unique_ptr<DBSession> session(conn->openSession()); state.ResumeTiming(); // 恢复计时,仅统计核心操作 // 核心基准测试代码 for (auto _ : state) { session->insertOp(/* 测试参数 */); } // 清理操作:排除计时 state.PauseTiming(); session->close(); if (state.thread_index() == 0) { conn->close(); } } // 示例:用4线程运行基准测试 BENCHMARK(BM_db_insert)->Threads(4); BENCHMARK_MAIN();
额外优化建议
- 避免IO操作:基准测试中不要打印日志,IO会严重干扰性能数据的准确性;
- 预生成测试数据:如果insertOp需要不同的参数(如递增ID),提前在循环外生成数据,避免数据生成计入基准时间;
- 参数化测试:用
BENCHMARK(BM_db_insert)->Arg(...)测试不同线程数、数据量下的性能; - 资源自动管理:用
std::unique_ptr/std::shared_ptr管理连接和会话,避免内存泄漏; - 预热优化:如果存储引擎需要预热,可以在初始化阶段添加预热逻辑,确保基准测试在稳定状态下运行。
内容的提问来源于stack exchange,提问作者zhihuifan
相关产品推荐
相关产品推荐

