我有一个线程池,每个线程包含一个计数器(基本上是TLS).
需要主线程通过计算所有线程局部计数器的总和来频繁更新.
大多数情况下,每个线程都会增加自己的计数器,因此不需要同步.
但是在主线程更新的时候,我当然需要某种同步.
我提出了MSVS内在函数(_InterlockedXXX函数),它表现出很好的性能(在我的测试中大约0.8秒)
但是,它将我的代码限制为MSVC编译器和X86 / AMD64平台,但是有一种C -portable方法吗?
>我尝试将int类型更改为std :: atomic< int>对于计数器,使用std :: memory_order_relaxed进行递增但这个解决方案非常慢! (~4s)
>当使用基本成员std :: atomic< T> :: _ My_val时,我会按照我想要的方式非原子地访问该值,但它也不可移植,所以问题是相同的……
>使用单个std :: atomic< int>由于争用率高(约10秒),所有线程共享的速度更慢
你有什么想法吗?也许我应该使用库(boost)?还是写我自己的课?
最佳答案 std :: atomic< int> :: fetch_add(1,std :: memory_order_relaxed)与_InterlockedIncrement一样快.
Visual Studio编译前者以锁定添加$1(或等效物),后者编译锁定inc,但执行时间没有差别;在我的系统(Core i5 @ 3.30 GHz)上,每个采用5630 ps / op,大约18.5个周期.
Microbenchmark使用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