我正在使用Google基准测试对我们软件中的某些功能进行基准测试。假设函数签名如下所示。返回类型可以是任何其他派生数据类型。
std::map<uint32_t, bool> func(Obj& o1, Obj& o2);
基准测试函数如下所示。
static void BM_Func(benchmark::State& state) {
// Prepare the objects o1 and o2
for (auto _ : state)
func(Obj& o1, Obj& o2);
}
BENCHMARK(BM_Func);
BENCHMARK_MAIN();
现在,代码编译完毕,我能够收集基准测试结果。但是,我有以下问题。
- 返回值会发生什么情况?如果我真的应该被打扰吗 是否不再在基准测试函数中的任何地方使用这些值?
- 我应该像这样调用函数
benchmark::DoNotOptimize( func(Obj& o1, Obj& o2) );
以避免优化吗?我真的不明白什么时候用benchmark::DoNotOptimize
调用函数
不使用benchmark::DoNotOptimize
的危险在于编译器可能会意识到func
绝对没有副作用。然后它会正确地得出结论,您的代码等效于for (auto _ : state) /* do nothing */;
.你当然不想什么都衡量。
使用benchmark::DoNotOptimize
会阻止编译器实现上述目标。它别无选择,只能实际调用func
来获取结果对象(尽管相同的注意事项适用 - 如果它可以内联func
并且func
总是返回例如true
,那么其余的func
可能会被优化出来(。
如果返回的对象很大,则销毁它可能需要有意义的时间。由于这发生在代码的基准测试循环中,因此这次将包含在基准测试中。不过,避免这种情况非常重要,并且该函数的任何"真实"用户也必须承担这段时间,因此答案是"这些对象不会发生任何异常情况"。
你必须记住,有"好像规则"。因此,编译器可以对代码执行任何操作,只要执行的可见效果保持不变。
在性能测量的情况下,编译器可能会删除被测函数,因为删除它不会影响执行的可见效果。
因此,建议测试应如下所示:
static void BM_Func(benchmark::State& state) {
// Prepare the objects o1 and o2
for (auto _ : state) {
auto result = func(Obj& o1, Obj& o2);
benchmark::DoNotOptimize(result);
}
}
benchmark::DoNotOptimize
将向编译器隐藏结果实际上未使用的事实。
请注意,基准测试必须使用与生产版本完全相同的优化标志来构建。因此,必须启用优化,并且过时的(从编译器的角度来看(代码将被删除。
顺便说一句,有一个很好的在线工具快速工作台,它使用 Google 基准测试,请注意,它还显示程序集结果,以便能够验证编译器优化的内容(以防某些代码被删除(。