3

我正在对我的处理器上的一些示例函数进行基准测试,每个内核都以 2 GHz 运行。以下是进行基准测试的功能。此外,可在快速工作台上使用

#include <stdlib.h>
#include <time.h>
#include <memory>

class Base
{
  public:       
   virtual int addNumVirt( int x ) { return (i + x); }
   int addNum( int x ) { return (x + i); }
   virtual ~Base() {}

  private:
   uint32_t i{10};
};

class Derived : public Base
{
  public:
   // Overrides of virtual functions are always virtual
   int addNumVirt( int x ) { return (x + i); }
   int addNum( int x ) { return (x + i); }

  private:
   uint32_t i{20};
};

static void BM_nonVirtualFunc(benchmark::State &state)
{
 srand(time(0));
 volatile int x = rand();
 std::unique_ptr<Derived> derived = std::make_unique<Derived>();
 for (auto _ : state)
 {
   auto result = derived->addNum( x );
   benchmark::DoNotOptimize(result);
 }
}
BENCHMARK(BM_nonVirtualFunc);

static void BM_virtualFunc(benchmark::State &state)
{
 srand(time(0));
 volatile int x = rand();
 std::unique_ptr<Base> derived = std::make_unique<Derived>();
 for (auto _ : state)
 {
   auto result = derived->addNumVirt( x );
   benchmark::DoNotOptimize(result);
 }
}
BENCHMARK(BM_virtualFunc);

static void StringCreation(benchmark::State& state) {
  // Code inside this loop is measured repeatedly
  for (auto _ : state) {
    std::string created_string("hello");
    // Make sure the variable is not optimized away by compiler
    benchmark::DoNotOptimize(created_string);
  }
}
// Register the function as a benchmark
BENCHMARK(StringCreation);

static void StringCopy(benchmark::State& state) {
  // Code before the loop is not measured
  std::string x = "hello";
  for (auto _ : state) {
    std::string copy(x);
  }
}
BENCHMARK(StringCopy);

以下是 Google 基准测试结果。

Run on (64 X 2000 MHz CPU s)
CPU Caches:
  L1 Data 32K (x32)
  L1 Instruction 64K (x32)
  L2 Unified 512K (x32)
  L3 Unified 8192K (x8)
Load Average: 0.08, 0.04, 0.00
------------------------------------------------------------
Benchmark                  Time             CPU   Iterations
------------------------------------------------------------
BM_nonVirtualFunc      0.490 ns        0.490 ns   1000000000
BM_virtualFunc         0.858 ns        0.858 ns    825026009
StringCreation          2.74 ns         2.74 ns    253578500
BM_StringCopy           5.24 ns         5.24 ns    132874574

结果表明,执行时间0.490 ns0.858 ns前两个函数。但是,我不明白的是,如果我的核心运行在 2 GHz,这意味着一个周期是0.5 ns,这使得结果看起来不合理。

我知道显示的结果是迭代次数的平均值。而如此短的执行时间意味着大多数示例都在下面0.5 ns

我错过了什么?

编辑1:i从评论看来, 添加一个常量似乎x不是一个好主意。事实上,我是从调用std::cout虚函数和非虚函数开始的。这有助于我理解虚函数不是内联的,调用需要在运行时解决。

但是,在功能中具有输出进行基准测试在终端上看起来并不好。(有没有办法从 Godbolt 分享我的代码?)任何人都可以提出替代方法来在函数内打印一些东西吗?

4

1 回答 1

1

现代编译器只是做了很棒的事情。并不总是最可预测的事情,但通常是好事。您可以通过按照建议观看 ASM 或通过降低优化级别来看到这一点。Optim=1 使 nonVirtualFunc 在 CPU 时间方面与 virtualFunc 等效,而 optim=0 将您的所有功能提高到相似的水平(编辑:当然是一种糟糕的方式;不要这样做以实际得出性能结论)。

是的,当我第一次使用 QuickBench 时,我也对“DoNotOptimize”感到困惑。他们最好将其称为“UseResult()”,以表明它在进行基准测试时实际上打算假装什么。

于 2019-11-29T13:17:12.230 回答