在这种无竞争的情况下,为什么原子比锁定慢得多?

Pal*_*han 2 c++ concurrency performance locking atomic

我用原子而不是锁写了一些东西,并且在我的情况下,我写了下面这个迷你测试的速度慢得多:

#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);
}
Run Code Online (Sandbox Code Playgroud)

如果我取消注释atomicPush操作并运行测试,time(1)我得到如下输出:

real    0m0.027s
user    0m0.022s
sys     0m0.005s
Run Code Online (Sandbox Code Playgroud)

如果我运行循环调用原子的东西(看似不必要的操作是因为我希望我的函数尽可能多地看起来像我的更大的代码那样)我得到如下输出:

real    0m0.046s
user    0m0.043s
sys     0m0.003s
Run Code Online (Sandbox Code Playgroud)

我不确定为什么会发生这种情况,因为在这种情况下,我希望原子比锁更快...

当我用-O3编译时,我看到锁和原子更新如下:

lock:
    real    0m0.024s
    user    0m0.022s
    sys     0m0.001s

atomic:    
    real    0m0.013s
    user    0m0.011s
    sys     0m0.002s
Run Code Online (Sandbox Code Playgroud)

在我的大型应用程序中虽然锁定(单线程测试)的性能仍然做得更好,尽管...

Ker*_* SB 6

无争用的互斥锁可以非常快速地锁定和解锁.使用原子变量,您总是会支付一定的内存同步惩罚(特别是因为您甚至没有使用轻松的排序).

您的测试用例过于天真,无法使用.您必须测试严重争用的数据访问方案.

通常,原子慢(它们妨碍了巧妙的内部重新排序,流水线操作和缓存),但它们允许无锁代码,确保整个程序可以取得一些进展.相比之下,如果你在拿着锁时换掉,每个人都要等.