我使用原子而不是锁来写一些东西,并且在我的情况下,我写了下面这个迷你测试的速度慢得多:
#include <pthread.h>
#include <vector>
struct test
{
test(size_t size) : index_(0), size_(size), vec2_(size)
{
vec_.reserve(size_);
pthread_mutexattr_init(&attrs_);
pthread_mutexattr_setpshared(&attrs_, PTHREAD_PROCESS_PRIVATE);
pthread_mutexattr_settype(&attrs_, PTHREAD_MUTEX_ADAPTIVE_NP);
pthread_mutex_init(&lock_, &attrs_);
}
void lockedPush(int i);
void atomicPush(int* i);
size_t index_;
size_t size_;
std::vector<int> vec_;
std::vector<int> vec2_;
pthread_mutexattr_t attrs_;
pthread_mutex_t lock_;
};
void test::lockedPush(int i)
{
pthread_mutex_lock(&lock_);
vec_.push_back(i);
pthread_mutex_unlock(&lock_);
}
void test::atomicPush(int* i)
{
int ii = (int) (i - &vec2_.front());
size_t index = __sync_fetch_and_add(&index_, 1);
vec2_[index & (size_ - 1)] = ii;
}
int main(int argc, char** argv)
{
const size_t N = 1048576;
test t(N);
// for (int i = 0; i < N; ++i)
// t.lockedPush(i);
for (int i = 0; i < N; ++i)
t.atomicPush(&i);
}
如果我取消注释atomicPush操作并使用time(1)
运行测试,我会得到如下输出:
real 0m0.027s
user 0m0.022s
sys 0m0.005s
如果我运行循环调用原子的东西(看似不必要的操作就在那里,因为我希望我的函数尽可能多地看起来像我的更大的代码那样)我得到如下输出:
real 0m0.046s
user 0m0.043s
sys 0m0.003s
我不确定为什么会发生这种情况,因为在这种情况下,我希望原子比锁更快......
当我使用-O3编译时,我看到锁定和原子更新如下:
lock:
real 0m0.024s
user 0m0.022s
sys 0m0.001s
atomic:
real 0m0.013s
user 0m0.011s
sys 0m0.002s
在我较大的应用程序中,虽然锁定(单线程测试)的性能仍然在做得更好,不管它是什么..
答案 0 :(得分:6)
无争用互斥锁的锁定和解锁速度非常快。使用原子变量,你总是支付一定的内存同步惩罚(特别是因为你甚至没有使用轻松的排序)。
您的测试用例过于天真,无法使用。您必须测试严重争用的数据访问方案。
通常,原子 缓慢(它们阻碍了内部重新排序,流水线操作和缓存),但它们允许无锁代码,确保整个程序可以使 >一些进展。相比之下,如果您在持有锁定时被换出,每个人都必须等待。
答案 1 :(得分:1)
只需添加第一个答案,当您执行__sync_fetch_and_add
时,您实际上会强制执行特定的代码排序。来自documentation
调用此函数时会创建一个完整的内存屏障
中央处理单元(CPU)或编译器,对屏障指令之前和之后发出的内存操作强制执行排序约束
即使你的工作是原子的,你也可能通过强制指令的排序而失去编译器优化。