我创建了一个简单的测试,以检查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
答案 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。