使用 Google 基准时 return 值会发生什么变化?
What happens to the return values while using Google benchmark?
我正在使用 Google-benchmark 对我们软件中的某些功能进行基准测试。让我们假设函数签名如下所示。 return 类型可以是任何其他派生数据类型。
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();
现在,代码编译完成,我可以收集基准测试结果了。但是,我有以下问题。
- return 值发生了什么变化?如果我
我没有在基准函数中的任何地方再次使用这些值吗?
- 我应该像这样
benchmark::DoNotOptimize( func(Obj& o1, Obj& o2) );
调用函数来避免优化吗?我真的不明白什么时候用 benchmark::DoNotOptimize
调用函数
你要记住还有“AS IF RULE”。因此,只要执行的可见效果保持不变,编译器就可以对代码做任何事情。
在性能测量的情况下,存在编译器删除被测函数的风险,因为删除它不会对执行的可见效果产生影响。
因此建议您的测试应如下所示:
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
将对编译器隐藏结果实际上未被使用的事实。
请注意,必须使用与生产版本完全相同的优化标志来构建基准测试。因此必须启用优化并删除过时的(从编译器的角度来看)代码。
顺便说一句,有一个很好的在线工具 quick-bench,它使用 google 基准测试,请注意它还显示汇编结果,以便能够验证编译器优化了什么(以防某些代码被删除) .
不 使用benchmark::DoNotOptimize
的危险在于编译器可能意识到func
绝对没有副作用。然后它会正确地断定您的代码等同于 for (auto _ : state) /* do nothing */;
。你当然不想测量任何东西。
使用benchmark::DoNotOptimize
会阻止编译器实现上述实现。它别无选择,只能实际调用 func
来获取结果对象(尽管同样的考虑适用 - 如果它可以内联 func
和 func
总是 returns 例如 true
,那么 func
的 rest 可能会被优化掉)。
如果返回的对象很大,那么销毁它可能需要很长时间。由于这发生在您代码的基准测试循环中,因此这次将包含在您的基准测试中。避免这种情况非常重要,而且该函数的任何 "real" 用户也必须承担这个时间,所以答案是 "nothing extraordinary happens with those objects".
我正在使用 Google-benchmark 对我们软件中的某些功能进行基准测试。让我们假设函数签名如下所示。 return 类型可以是任何其他派生数据类型。
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();
现在,代码编译完成,我可以收集基准测试结果了。但是,我有以下问题。
- return 值发生了什么变化?如果我 我没有在基准函数中的任何地方再次使用这些值吗?
- 我应该像这样
benchmark::DoNotOptimize( func(Obj& o1, Obj& o2) );
调用函数来避免优化吗?我真的不明白什么时候用benchmark::DoNotOptimize
调用函数
你要记住还有“AS IF RULE”。因此,只要执行的可见效果保持不变,编译器就可以对代码做任何事情。
在性能测量的情况下,存在编译器删除被测函数的风险,因为删除它不会对执行的可见效果产生影响。
因此建议您的测试应如下所示:
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
将对编译器隐藏结果实际上未被使用的事实。
请注意,必须使用与生产版本完全相同的优化标志来构建基准测试。因此必须启用优化并删除过时的(从编译器的角度来看)代码。
顺便说一句,有一个很好的在线工具 quick-bench,它使用 google 基准测试,请注意它还显示汇编结果,以便能够验证编译器优化了什么(以防某些代码被删除) .
不 使用benchmark::DoNotOptimize
的危险在于编译器可能意识到func
绝对没有副作用。然后它会正确地断定您的代码等同于 for (auto _ : state) /* do nothing */;
。你当然不想测量任何东西。
使用benchmark::DoNotOptimize
会阻止编译器实现上述实现。它别无选择,只能实际调用 func
来获取结果对象(尽管同样的考虑适用 - 如果它可以内联 func
和 func
总是 returns 例如 true
,那么 func
的 rest 可能会被优化掉)。
如果返回的对象很大,那么销毁它可能需要很长时间。由于这发生在您代码的基准测试循环中,因此这次将包含在您的基准测试中。避免这种情况非常重要,而且该函数的任何 "real" 用户也必须承担这个时间,所以答案是 "nothing extraordinary happens with those objects".