为什么在这个无可争议的情况下,原子比锁慢得多
Why are the atomics much slower than the lock in this uncontended case?
我用原子而不是锁写了一些东西,并且对它在我的情况下慢得多感到困惑,我写了以下迷你测试:
#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
在我的较大应用程序中,尽管锁的性能(单线程测试)仍然做得更好。
无竞争的互斥锁锁定和解锁速度极快。使用原子变量,您总是要付出一定的内存同步代价(特别是因为您甚至没有使用宽松排序)。
您的测试用例太天真了,没有用处。您必须测试争用严重的数据访问方案。
通常,原子学很慢(它们妨碍了巧妙的内部重新排序,流水线和缓存),但它们允许无锁代码,从而确保整个程序可以取得一些进展。相比之下,如果你在拿锁时被换掉,每个人都必须等待。
补充一下第一个答案,当你做一个__sync_fetch_and_add
时,你实际上强制执行特定的代码排序。从文档
调用此函数时会创建一个完整的内存屏障
记忆障碍是
中央处理器 (CPU) 或编译器,用于对屏障指令之前和之后发出的内存操作强制执行排序约束
即使您的工作是原子的,您也可能通过强制指令排序而丢失编译器优化。
相关文章:
- 在没有太多条件句的情况下,我如何避免被零除
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 在未初始化映射的情况下,将值插入到映射的映射中
- 是默认情况下分配给char数组常量的值
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 如何在不产生任何垃圾的情况下获得C中的像素
- 在已经使用Git的情况下减少编译时间
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 如何在没有信号的情况下从C++执行QML插槽
- 如何在不知道向量大小的情况下输入向量内部的向量?
- 为什么在某些情况下不写入此文件?
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 在没有Xcode的情况下在Mac捆绑包中嵌入框架
- UE4-如何在给定4个屏幕坐标的情况下缩放纹理或材质
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 在C++中如何在没有pow的情况下进行基础计算
- 松弛原子与无同步情况下的记忆连贯性
- 在 Windows 上,是否可以让 dll 在不使用 PATH 环境变量的情况下在另一个文件夹中查找依赖项?
- 我是c ++的新手,你能解释一下在这种情况下的指针吗
- 为什么在这个无可争议的情况下,原子比锁慢得多