为什么memory_order_relaxed性能与memory_order_seq_cst相同

时间:2018-12-16 18:15:14

标签: c++ c++11

我创建了一个简单的测试,以检查std::memory_order_relaxed的增量如何比std::memory_order_seq_cst的值更快。但是,两种情况下的性能都相同。
我的编译器:gcc版本7.3.0(Ubuntu 7.3.0-27ubuntu1〜18.04)
构建参数:g ++ -m64 -O3 main.cpp -std = c ++ 17 -lpthread
CPU:Intel(R)CoreTM i7-2670QM CPU @ 2.20GHz,4核,每核2个线程
测试代码:

atomic<int>

为什么#include <vector> #include <iostream> #include <thread> #include <atomic> #include <chrono> #include <functional> std::atomic<int> cnt = {0}; void run_test_order_relaxed() { std::vector<std::thread> v; for (int n = 0; n < 4; ++n) { v.emplace_back([]() { for (int n = 0; n < 30000000; ++n) { cnt.fetch_add(1, std::memory_order_relaxed); } }); } std::cout << "rel: " << cnt.load(std::memory_order_relaxed); for (auto& t : v) t.join(); } void run_test_order_cst() { std::vector<std::thread> v; for (int n = 0; n < 4; ++n) { v.emplace_back([]() { for (int n = 0; n < 30000000; ++n) { cnt.fetch_add(1, std::memory_order_seq_cst); } }); } std::cout << "cst: " << cnt.load(std::memory_order_seq_cst); for (auto& t : v) t.join(); } void measure_duration(const std::function<void()>& func) { using namespace std::chrono; high_resolution_clock::time_point t1 = high_resolution_clock::now(); func(); high_resolution_clock::time_point t2 = high_resolution_clock::now(); auto duration = duration_cast<milliseconds>( t2 - t1 ).count(); std::cout << " duration: " << duration << "ms" << std::endl; } int main() { measure_duration(&run_test_order_relaxed); measure_duration(&run_test_order_cst); return 0; } std::memory_order_relaxed总是产生几乎相同的结果?
结果:
rel:2411持续时间:4440ms
cst:120000164持续时间:4443ms

1 个答案:

答案 0 :(得分:6)

无论内存顺序设置如何,您都需要在两个循环中进行原子操作。事实证明,对于在大多数情况下固有地有序排列的x86处理器,这导致对每个fetch_add:lock xadd使用相同的asm代码。 x86处理器上的这种原子操作始终是顺序一致的,因此在指定宽松的内存顺序时,这里没有优化机会。

使用宽松的内存顺序可以进一步优化周围的操作,但是您的代码没有提供任何进一步的优化机会,因此发出的代码是相同的。请注意,使用弱排序的处理器(例如ARM)或在循环内进行更多的数据操作(可能提供更多的重新排序机会)时,结果可能会有所不同。

来自cppreference(我的斜体字):

  

std :: memory_order指定在原子操作周围 进行常规非原子内存访问的顺序。

Memory Models for C/C++ Programmers号论文对此提供了更多的详细信息。

请注意,重复运行原子基准测试或在不同的x86处理器上运行原子基准测试(甚至由同一制造商)可能会导致截然不同的结果,因为线程可能无法平等地分布在所有内核上,并且缓存延迟是受本地内核,同一芯片上的另一个内核还是另一个芯片上的内核的影响。它也受特定处理器如何处理潜在一致性冲突的影响。此外,级别1,级别2和级别3缓存的行为与ram一样,因此,数据集的总大小也会产生重大影响。参见Evaluating the Cost of Atomic Operations on Modern Architectures