为什么SpinLock的实现中应该加上PAUSE指令?

来源:互联网 发布:触乐 知乎专栏 编辑:程序博客网 时间:2024/06/06 02:07

当spinlock执行lock()获得锁失败后会进行busy loop(参考这段代码),不断检测锁状态,尝试获得锁。这么做有一个缺陷:频繁的检测会让流水线上充满了读操作。另外一个线程往流水线上丢入一个锁变量写操作的时候,必须对流水线进行重排,因为CPU必须保证所有读操作读到正确的值。流水线重排十分耗时,影响lock()的性能。

        inline int rdlock()        {          int ret = common::OB_SUCCESS;          int64_t tmp = 0;          while (true)          {            tmp = ref_cnt_;            if (0 > tmp || 0 < wait_write_)            {              // 写优先              continue;            }            else            {              int64_t nv = tmp + 1;              if (tmp == (int64_t)atomic_compare_exchange((uint64_t*)&ref_cnt_, nv, tmp))              {                break;              }            }          }          return ret;        };


为了解决这个问题,intel发明了pause指令。这个指令的本质功能:让加锁失败时cpu睡眠30个(about)clock,从而使得读操作的频率低很多。流水线重排的代价也会小很多。


参考这位同学的博客,讲得很好很清晰:

http://kb.cnblogs.com/page/105657/

  4, Pause指令解释(from intel):

  Description

  Improves the performance of spin-wait loops. When executing a “spin-wait loop,” a Pentium 4 or Intel Xeon processor suffers a severe performance penalty when exiting the loop because it detects a possible memory order violation. The PAUSE instruction provides a hint to the processor that the code sequence is a spin-wait loop. The processor uses this hint to avoid the memory order violation in most situations, which greatly improves processor performance. For this reason, it is recommended that a PAUSE instruction be placed in all spin-wait loops.

  提升spin-wait-loop的性能,当执行spin-wait循环的时候,笨死和小强处理器会因为在退出循环的时候检测到memory order violation而导致严重的性能损失,pause指令就相当于提示处理器哥目前处于spin-wait中。在绝大多数情况下,处理器根据这个提示来避免violation,藉此大幅提高性能,由于这个原因,我们建议在spin-wait中加上一个pause指令。

  名词解释(以下为本人猜想):memory order violation,直译为-内存访问顺序冲突,当处理器在(out of order)乱序执行的流水线上去内存load某个内存地址的值(此处是lock)的时候,发现这个值正在被store,而且store本身就在load之前,对于处理器来说,这就是一个hazard,流水流不起来。

  在本文中,具体是指当一个获得锁的工作线程W从临界区退出,在调用unlock释放锁的时候,有若干个等待线程S都在自旋检测锁是否可用,此时W线程会产生一个store指令,若干个S线程会产生很多load指令,在store之后的load指令要等待store在流水线上执行完毕才能执行,由于处理器是乱序执行,在没有store指令之前,处理器对多个没有依赖的load是可以随机乱序执行的,当有了store指令之后,需要reorder重新排序执行,此时会严重影响处理器性能,按照intel的说法,会带来25倍的性能损失。Pause指令的作用就是减少并行load的数量,从而减少reorder时所耗时间。




晓楚(17:40:19):
这篇文章很好,基本解释清楚了spin_lock中为什么要加pause。中文的,大家都看得懂。
http://kb.cnblogs.com/page/105657/

 (17:49:28):
对失败的加锁行为进行惩罚(failure penalty),让等待时间和失败次数成正比,即失败次数越多等待时间越长、执行的pause指令越多。
这个我们也可以用吗?

到底最大pause次数是多少需要试验来支撑,目前是4次;个人感觉pause的处理器实现粒度还是比较粗的,应该是intel的一个经验值,接下来的试验可以用nop来代替pause,这样得出来的数据应该会更为平滑一些,控制也更为细腻。
nop是什么指令?

晓楚(17:51:21):
我觉得没必要,这个做法太复杂了。用pause就足够了。
nop是让cpu 空转1个clock的指令。什么都不做。

 (17:51:59):
其实可以测试。
临界区大小,我们一般是多少代码?

 (17:59:29):
看他的实验结果,提升并不明显,5%的样子,还是在冲突特别大的情况下,好像不值得。
要优化spin lock,主要还是避免大量的线程在同一个cache line里spin。我以前测过,要是以浪费内存为代价,可以很轻松提升40%的样子。同样的思路去优化spin_rwlock的读锁(以降低写锁性能为代价),让不同的读者修改自己线程局部的引用计数,读锁性能可以提升几倍。不过现在性能还不在这里。

 (18:02:28):
恩,支持。现在性能还不在这里。我们保持关注,先从收益大的地方入手。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 去痣留下的红印怎么办 激光点痦子留疤怎么办 激光点痣的疤痕怎么办 做完眉毛碰水了怎么办 脸上疤掉了有坑怎么办 结痂不小心抠掉怎么办 脸上肉松弛怎么办19岁 点痣留下来的疤怎么办 激光祛斑的红印怎么办 脸上疤掉了红印怎么办 痘痘发炎了红肿怎么办 脸上的斑越来越多了怎么办 点痣留下的疤痕怎么办 额头又高又大怎么办 脸太长额头太高怎么办 动车因台风停运怎么办 爸妈50了要离婚怎么办 鸿利彩票黑了钱怎么办 忘了锁屏图案怎么办 黄金被水银沾上怎么办 被股东了我该怎么办 异地恋没话题聊怎么办 谈了半年分手了怎么办 博士6年没毕业怎么办 发现孩子早恋家长应该怎么办 异地恋想嘿嘿嘿怎么办 妈妈溜冰溜大了怎么办 皮鞋被雨水泡了怎么办 老婆提出离婚我不想离怎么办 极度缺爱的人怎么办 生二胎住院大宝怎么办 爸妈偏心我该怎么办 无创21体高风险怎么办 无创检查高风险怎么办 唐氏筛查21三体高危怎么办 唐筛年龄高风险怎么办 21三体综合症高风险怎么办 朋友深陷李强365怎么办 飞机上烟瘾犯了怎么办 怀孕一个月吸烟了怎么办 烟瘾犯了没烟怎么办