4

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

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

  1. 返回值会发生什么?如果我不再在基准函数中的任何地方使用这些值,我是否应该为此烦恼?
  2. 我应该像这样调用函数benchmark::DoNotOptimize( func(Obj& o1, Obj& o2) );以避免优化吗?我真的不明白何时调用该函数benchmark::DoNotOptimize
4

2 回答 2

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,注意它还显示了汇编结果,以便能够验证编译器优化了什么(以防某些代码被删除)。

于 2019-11-28T16:15:02.200 回答
3

使用的危险benchmark::DoNotOptimize在于编译器可能会意识到它func绝对没有副作用。然后它会正确地断定您的代码等同于for (auto _ : state) /* do nothing */;. 你当然不想测量任何东西。

使用benchmark::DoNotOptimize阻止编译器进行上述实现。它别无选择,只能实际调用func以获取结果对象(尽管适用相同的考虑 - 如果它可以内联funcfunc始终返回 eg true,那么其余部分func可能会得到优化)。

如果返回的对象很大,那么销毁它可能需要相当长的时间。由于这发生在代码中的基准测试循环内,因此这次将包含在您的基准测试中。避免这种情况是非常重要的,并且该函数的任何“真正”用户也将不得不招致这个时间,所以答案是“这些对象不会发生任何异常情况”。

于 2019-11-28T16:15:13.280 回答