我创建了一个简单的测试来检查怎么std::memory_order_relaxed快于std::memory_order_seq_cst对价值atomic<int>增量.然而,两种情况的表现都相同.
我的编译器:gcc版本7.3.0(Ubuntu 7.3.0-27ubuntu1~18.04)
构建参数:g ++ -m64 -O3 main.cpp -std = c ++ 17 -lpthread
CPU:Intel(R)Core(TM)i7- 2670QM CPU @ 2.20GHz,4核,每核2线程
测试代码:
#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;
}
Run Code Online (Sandbox Code Playgroud)
为什么std::memory_order_relaxed并且std::memory_order_seq_cst总能产生几乎相同的结果?
结果:
rel:2411持续时间:4440ms
cst:120000164持续时间:4443ms
无论内存顺序设置如何,您都需要在两个循环中进行原子操作.事实证明,对于在大多数情况下固有强烈排序的x86处理器,这导致每个fetch_add使用相同的asm代码:lock xadd.x86处理器上的此原子操作始终是顺序一致的,因此在指定宽松内存顺序时,此处没有优化机会.
使用宽松的内存顺序可以进一步优化周围的操作,但是您的代码不提供任何进一步的优化机会,因此发出的代码是相同的.请注意,结果可能与弱有序处理器(例如,ARM)或循环内的更多数据操作(可能提供更多重新排序机会)不同.
来自cppreference(我的斜体):
std :: memory_order指定如何围绕原子操作对常规非原子内存访问进行排序.
本文对C记忆模型/ C++程序员提供许多在此的更多细节.
作为旁注,反复运行原子基准测试或在不同的x86处理器(即使是同一制造商)上运行它们可能会导致显着不同的结果,因为线程可能不会平均分布在所有核心上,并且缓存延迟会受到影响它是本地核心,同一芯片上的另一个核心,或另一个芯片.它还受特定处理器如何处理潜在一致性冲突的影响.此外,1级,2级和3级缓存的行为与ram不同,因此数据集的总大小也具有显着的影响.请参阅评估现代架构的原子操作成本.