使用 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();

现在,代码编译完成,我可以收集基准测试结果了。但是,我有以下问题。

  1. return 值发生了什么变化?如果我 我没有在基准函数中的任何地方再次使用这些值吗?
  2. 我应该像这样 benchmark::DoNotOptimize( func(Obj& o1, Obj& o2) ); 调用函数来避免优化吗?我真的不明白什么时候用 benchmark::DoNotOptimize
  3. 调用函数

你要记住还有“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 来获取结果对象(尽管同样的考虑适用 - 如果它可以内联 funcfunc 总是 returns 例如 true,那么 funcrest 可能会被优化掉)。

如果返回的对象很大,那么销毁它可能需要很长时间。由于这发生在您代码的基准测试循环中,因此这次将包含在您的基准测试中。避免这种情况非常重要,而且该函数的任何 "real" 用户也必须承担这个时间,所以答案是 "nothing extraordinary happens with those objects".