2015-04-05 506 views
0

对于简单任务,自旋锁应具有比互斥锁更好的性能。然而,在这种简单的测试(8个线程递增计数器)区,结果表明是不同的:C++:自旋锁或互斥对比(简单计算)

#include <iostream> 
#include <thread> 
#include <mutex> 
#include <atomic> 
#include <vector> 

using namespace std; 

class SpinLock { 
private: 
    atomic_flag lck = ATOMIC_FLAG_INIT; 
public: 
    void lock() { while(lck.test_and_set(memory_order_acquire)) {} } 
    void unlock() { lck.clear(memory_order_release); } 
}; 

int total = 0; 

#ifdef SPINLOCK 
SpinLock my_lock; 
#else 
mutex my_lock; 
#endif 

void foo(int n) 
{ 
    for(int i = 0; i < 10000000; ++i) { 
#ifdef SPINLOCK 
     lock_guard<SpinLock> lck(my_lock); 
#else 
     lock_guard<mutex> lck(my_lock); 
#endif 
     ++total; 
    } 
} 

int main() 
{ 
    vector<thread> v; 

    for(int i = 0; i < 8; ++i) 
     v.emplace_back(foo, i); 

    for(auto& t : v) 
     t.join(); 

    cout << "total: " << total << endl; 
    return 0; 
} 

要测试自旋锁:

$ g++ -DSPINLOCK -std=c++11 -Wall -pthread test.cc 
$ time ./a.out 
total: 80000000 
real 0m18.206s 
user 2m17.792s 
sys  0m0.003s 

要测试互斥:

$ g++ -std=c++11 -Wall -pthread test.cc 
$ time ./a.out 
total: 80000000 
real 0m9.483s 
user 0m6.451s 
sys  1m6.043s 

的结果显示互斥锁比自旋锁快近两倍。自旋锁在“用户cpu”中花费最多的时间,互斥锁花费大部分时间在“sys cpu”中。互斥体是如何实现的,我应该在这种简单的计算中使用互斥锁而不是自旋锁吗?任何人都可以解释结果吗?

g ++是4.8.2,操作系统是红帽企业Linux 7.

谢谢。

回答

2

一些注意事项:

  1. time实用程序的输出显示的时间是你的线程使用,而不是实际时间的CPU时间。即使在等待期间,Spinlock也会使用cpu,而内核互斥体将在等待期间执行其他进程中的其他线程,而不是为CPU时间为您的进程计费,除了用于实际执行调度的进程(您在互斥体case中的sys行中看到的那个)。

  2. 出于上述的相同原因,它可能是你从开始过程到结束等待的总时间在螺旋锁定情况下更快,但是你的cpu可能有更高的使用率,这就是你观察到的。

  3. 线程可能是一个很好的选择,如果您的碰撞几率很低,那就是线程花费在同步加载中的时间与它可以异步执行的加载相比较小。如果所有的负载都受到互斥体的保护,那么根本就只有使用线程的开销 - 你应该序列化它。

如果在碰撞的情况下同时具有低碰撞几率和较低等待时间,则自旋锁很好。在你的情况下,你有8个线程碰撞到相同的资源,然后要求资源在你释放它之后立即可用。这意味着平均有1个线程工作和7个spinlocking,将所用CPU的总时间用于单线程所需时间的8倍(如果您有一个8核心机器并且没有其他负载)。在互斥体的情况下,线程暂停并在资源可用时唤醒,所以没有等待的开销,但是锁定互斥体需要一些开销,因为在内核中要做的工作是跟踪哪些进程正在等待互斥锁,即使它不是太大,你正在做160万次互斥操作,总计到sys时间计费到你的过程

+0

感谢您的解释。 – user2847598 2015-04-05 14:00:56