我有一个线程池,每个线程都包含一个计数器(基本上是 TLS)。
主线程需要通过计算所有线程本地计数器的总和来频繁更新。
大多数时候,每个线程都会递增自己的计数器,因此不需要同步。
但是在主线程更新的时候,我当然需要某种同步。
我想出了 MSVS 内在函数(_InterlockedXXX
函数),它表现出了出色的性能(在我的测试中大约 0.8 秒)
但是,它将我的代码限制在 MSVC 编译器和 X86/AMD64 平台上,但是是否有一种 C++ 可移植的方法来做到这一点?
我尝试将 int 类型更改为
std::atomic<int>
对于柜台,使用std::memory_order_relaxed
对于增量,但这个解决方案非常慢! (~ 4 秒)使用基础成员时
std::atomic<T>::_My_val
,该值是按我希望的那样以非原子方式访问的,但它也不可移植,所以问题是一样的......使用单个
std::atomic<int>
由于竞争激烈(~ 10 秒),所有线程共享甚至更慢
你有什么想法吗?也许我应该使用图书馆(提升)?还是编写我自己的类?
最佳答案
std::atomic<int>::fetch_add(1, std::memory_order_relaxed)
与 _InterlockedIncrement
一样快.
Visual Studio 将前者编译为 lock add $1
(或等同物),后者为 lock inc
,但执行时间没有区别;在我的系统(Core i5 @3.30 GHz)上,每个都需要 5630 ps/op,大约 18.5 个周期。
使用 Benchpress 的微基准测试:
#define BENCHPRESS_CONFIG_MAIN
#include "benchpress/benchpress.hpp"
#include <atomic>
#include <intrin.h>
std::atomic<long> counter;
void f1(std::atomic<long>& counter) { counter.fetch_add(1, std::memory_order_relaxed); }
void f2(std::atomic<long>& counter) { _InterlockedIncrement((long*)&counter); }
BENCHMARK("fetch_add_1", [](benchpress::context* ctx) {
auto& c = counter; for (size_t i = 0; i < ctx->num_iterations(); ++i) { f1(c); }
})
BENCHMARK("intrin", [](benchpress::context* ctx) {
auto& c = counter; for (size_t i = 0; i < ctx->num_iterations(); ++i) { f2(c); }
})
输出:
fetch_add_1 200000000 5634 ps/op
intrin 200000000 5637 ps/op
关于c++ - 同时使用非原子和原子操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36544855/