参考:https://demin.ws/blog/english/2012/05/05/atomic-spinlock-mutex/
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
加锁解锁的性能是怎样的,成本有多高呢?
是不是很高,付出的成本和哪些地方有关系呢?
和使用的加锁方式有没有关系?
和使用加锁量的线程多少有没有关系?
和加锁内容多少有没有关系?
和调用频率多少有没有关系?
针对这些问题,咱们每个人也都会有各自的思考,本次期望研究其中的一项:加锁方式的成本。
通过参考一个2线程的加锁同步的结果,来看看对不同锁的效率,算是对于加锁方式做一个简单研究。
代码来自于https://demin.ws/blog/english/2012/05/05/atomic-spinlock-mutex/
在这个网址上,作者在约2012年之前提供了一个性能测试,测试的结果显示不同锁的性能是有所不同的。
注意:对于mutex当时的测试性能很差,这块可能受当时库版本的影响,在新版本测试时发现mutex性能有很大提升。
一个3千万数量级的加减法,2个线程来做,一个线程做1千万,一个线程做2千万,效率测试如下
Method Time (sec.)
No synchronization 0.070
LOCK 0.481 // asm(“LOCK”);
Atomic 0.457 // atomic_inc32(&value)
Spinlock 0.541 // std::lock_guardboost::detail::spinlock guard(lock);
Mutex 22.667 // std::lock_guardstd::mutex gurad(mutex);
注意:mutex效率当前效率和文章中测试时对比,性能提升很大,提升了约一个数量级。
无锁方式使用的代码
#include
#include
#include
volatile int value = 0;
int loop(bool inc, int limit) {
std::cout << "Started " << inc << " " << limit << std::endl;
for (int i = 0; i < limit; ++i) {
if (inc) {
++value;
} else {
--value;
}
}
return 0;
}
int main() {
auto f = std::async(std::launch::async, std::bind(loop, true, 20000000));
loop(false, 10000000);
f.wait();
std::cout << value << std::endl;
}
个人测试
一方面为了验证结果,一方面也期望对mutex重新测试一下,所以我这边也重新测试了一下几种情况的效率:
- 无锁
- 使用std::atomic自加自减
- 使用std::mutex加锁
本次分别在两个Linux服务器上分别做了一个简单验证:使用time方法来统计程序的运行cpu占用。
在一个linux服务器-cpu 2.80GHz多核服务器测试:
注意这个是两个线程自加自减的测试情况,线程增多时或加锁内容多时效率下降会很大。
- 无锁测试时间效率:
real 0m0.094s
user 0m0.147s
sys 0m0.002s - std::atomic测试时间效率:
real 0m0.404s
user 0m0.740s
sys 0m0.003s - std::mutex测试时间效率:
real 0m1.898s
user 0m2.242s
sys 0m1.308s
对比测试数据,无锁->Atomic->Mutex依次接近5倍时间递增关系:无锁情况1s可以执行约3-6亿次,Atomic-1s可以执行约5-9千万次,mutex-1s可以执行约1-1.5千万次。
如果换算每次计算所消耗的cpu时钟的话,无锁处理-约9个时钟频率/次,带Atomic处理-37个时钟频率时间/次,带Mutex处理-177个时钟频率时间/次。
在一个liunx服务器-cpu 2.40GHz双核服务器测试:
- 无锁测试时间效率:
real 0m0.045s
user 0m0.064s
sys 0m0.002s - std::atomic测试时间效率:
real 0m0.403s
user 0m0.741s
sys 0m0.004s - std::mutex测试时间效率:
real 0m2.273s
user 0m2.728s
sys 0m1.578s
对比在多核情况下,双核无锁情况执行效率更高,可能是调度成本比较低,这个服务器没有启别的服务;
atomic与mutex性能相近,这个相近也不难理解,带锁时,同一时间只有一个线程完成加锁变量的自加或自减。
同步 *** 作,对于cpu而言也是一种指令的执行,例如test and set, compare and swap,确保某些处理的原子性,然后基于处理结果再来保护更多内容的同步。
加锁解锁的成本不仅在同步指令的执行上,更重要的方面是执行等待上,执行等待有两种,一种是像spinlock一样占着cpu等待,一种是挂起cpu让出线程,两者都是有成本。
不同同步策略,区分场景来用。
感觉不同策略核心都是减少加锁内容与避免挂起,像atomic就是加锁内容极简的一个实现。
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)