加锁解锁性能

加锁解锁性能,第1张

参考: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重新测试一下,所以我这边也重新测试了一下几种情况的效率:

  1. 无锁
  2. 使用std::atomic自加自减
  3. 使用std::mutex加锁

本次分别在两个Linux服务器上分别做了一个简单验证:使用time方法来统计程序的运行cpu占用。

测试1:

在一个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个时钟频率时间/次。

测试2:

在一个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)

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/674679.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-19
下一篇 2022-04-19

发表评论

登录后才能评论

评论列表(0条)

保存