atomic fetch_add vs添加性能

Rom*_*man 7 c++ multithreading c++11

下面的代码演示了多线程编程的好奇心.特别std::memory_order_relaxed是单个线程中增量与常规增量的性能.我不明白为什么fetch_add(宽松)单线程比常规增量慢两倍.

static void BM_IncrementCounterLocal(benchmark::State& state) {
  volatile std::atomic_int val2;

  while (state.KeepRunning()) {
    for (int i = 0; i < 10; ++i) {
      DoNotOptimize(val2.fetch_add(1, std::memory_order_relaxed));
    }
  }
}
BENCHMARK(BM_IncrementCounterLocal)->ThreadRange(1, 8);

static void BM_IncrementCounterLocalInt(benchmark::State& state) {
  volatile int val3 = 0;

  while (state.KeepRunning()) {
    for (int i = 0; i < 10; ++i) {
      DoNotOptimize(++val3);
    }
  }
}
BENCHMARK(BM_IncrementCounterLocalInt)->ThreadRange(1, 8);
Run Code Online (Sandbox Code Playgroud)

输出:

      Benchmark                               Time(ns)    CPU(ns) Iterations
      ----------------------------------------------------------------------
      BM_IncrementCounterLocal/threads:1            59         60   11402509                                 
      BM_IncrementCounterLocal/threads:2            30         61   11284498                                 
      BM_IncrementCounterLocal/threads:4            19         62   11373100                                 
      BM_IncrementCounterLocal/threads:8            17         62   10491608

      BM_IncrementCounterLocalInt/threads:1         31         31   22592452                                 
      BM_IncrementCounterLocalInt/threads:2         15         31   22170842                                 
      BM_IncrementCounterLocalInt/threads:4          8         31   22214640                                 
      BM_IncrementCounterLocalInt/threads:8          9         31   21889704  

Mar*_*ica 0

本地版本不使用原子。(它使用的事实volatile是一个转移注意力的事实 -volatile在多线程代码中基本上没有任何意义)。

原子版本使用原子(!)。实际上只有一个线程将用于访问该变量,这一事实对 CPU 来说是不可见的,而且我对编译器没有发现它也并不感到惊讶。(没有必要浪费开发人员的精力来确定转换为 是否安全std::atomic_intint而实际上它几乎永远不会安全。atomic_int如果不需要从多个线程访问它,没有人会编写。)

因此,原子版本会遇到麻烦,要确保增量实际上是原子的,坦率地说,我很惊讶它只慢了 2 倍 - 我本来预计会慢 10 倍。