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

当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):
恩,支持。现在性能还不在这里。我们保持关注,先从收益大的地方入手。

 

 

 

posted @ 2013-04-03 19:23  xinyuyuanm  阅读(1666)  评论(0编辑  收藏  举报