我有一个线程池,每个线程都包含一个计数器(基本上是TLS )。
主线程需要通过计算所有线程本地计数器的总和来频繁更新。
大多数情况下,每个线程都会增加自己的计数器,因此不需要同步。
但是当主线程正在更新时,我当然需要某种同步。
我提出了MSVS的本质(_InterlockedXXX函数),它显示了很好的性能(在我的测试中~ 0.8 s)然而,它将我的代码限制在MSVC编译器和X86/ and 64平台上,但是有C++可移植的方法吗?
std::atomic<int>,增量使用std::memory_order_relaxed,但是这个解决方案非常慢!(~ 4s)std::atomic<T>::_My_val时,可以像我想的那样以非原子方式访问该值,但是它也不是可移植的,所以问题是相同的.std::atomic<int>甚至更慢。你有什么想法吗?也许我应该使用一个图书馆(boost)?还是写我自己的课?
发布于 2016-04-11 11:27:48
std::atomic<int>::fetch_add(1, std::memory_order_relaxed)和_InterlockedIncrement一样快。
Visual将前者编译为lock add $1 (或等效的),后者编译为lock inc,但执行时间没有差别;在my系统(Core i5 @3.30 GHz)上,每个系统的运行周期为5630 ps/op,约18.5个周期。
使用台式压力机的微基准测试
#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发布于 2016-04-11 11:24:00
我想出了这种适合我的实现方法。但是,我找不到编写semi_atomic<T>::Set()代码的方法
#include <atomic>
template <class T>
class semi_atomic<T> {
T Val;
std::atomic<T> AtomicVal;
semi_atomic<T>() : Val(0), AtomicVal(0) {}
// Increment has no need for synchronization.
inline T Increment() {
return ++Val;
}
// Store the non-atomic Value atomically and return it.
inline T Get() {
AtomicVal.store(Val, std::memory_order::memory_order_release);
return AtomicVal.load(std::memory_order::memory_order_relaxed);
}
// Load _Val into Val, but in an atomic way (?)
inline void Set(T _Val) {
_InterlockedExchange((volatile long*)&Val, _Val); // And with C++11 ??
}
}谢谢你,如果有什么不对劲的话告诉我!
发布于 2016-04-12 08:05:06
您肯定是对的:每个线程都需要一个std::atomic<int>才能实现可移植性,即使它在某种程度上很慢。
但是,在X86和AMD64体系结构中,它可以(非常)优化。
这是我得到的,sInt是一个签名32位或64位的人。
// Here's the magic
inline sInt MyInt::GetValue() {
return *(volatile sInt*)&Value;
}
// Interlocked intrinsic is atomic
inline void MyInt::SetValue(sInt _Value) {
#ifdef _M_IX86
_InterlockedExchange((volatile sInt *)&Value, _Value);
#else
_InterlockedExchange64((volatile sInt *)&Value, _Value);
#endif
}此代码将在MSVS中使用X86体系结构(GetValue()所需)。
https://stackoverflow.com/questions/36544855
复制相似问题