螺旋锁所需的最小X86组件是多少

时间:2014-04-08 16:58:21

标签: assembly x86 spinlock

在程序集中实现自旋锁。在这里,我发布了一个我想出的解决方案。这是对的吗?你知道一个较短的吗?

锁:

    mov ecx, 0
.loop:
    xchg [eax], ecx
    cmp ecx, 0
    je .loop

释放:

    lock dec dword [eax]

eax初始化为-1(表示锁定是免费的)。这适用于许多线程(不一定是2)。

3 个答案:

答案 0 :(得分:5)

最短可能是:

acquire:
    lock bts [eax],0
    jc acquire

release:
    mov [eax],0

为了提高性能,最好使用“测试,测试和设置”方法,并使用pause,如下所示:

acquire:
    lock bts [eax],0    ;Optimistic first attempt
    jnc l2              ;Success if acquired
l1:
    pause
    test [eax],1        
    jne l1              ;Don't attempt again unless there's a chance

    lock bts [eax],0    ;Attempt to acquire
    jc l1               ;Wait again if failed

l2:

release:
    mov [eax],0

对于调试,您可以添加额外的数据,以便更容易地检测到问题,例如:

acquire:
    lock bts [eax],31         ;Optimistic first attempt
    jnc l2                    ;Success if acquired

    mov ebx,[CPUnumber]
    lea ebx,[ebx+0x80000000]
    cmp [eax],ebx             ;Is the lock acquired by this CPU?
    je .bad                   ; yes, deadlock
    lock inc dword [eax+4]    ;Increase "lock contention counter"
l1:
    pause
    test [eax],0x80000000        
    jne l1                    ;Don't attempt again unless there's a chance

    lock bts [eax],31         ;Attempt to acquire
    jc l1                     ;Wait again if failed

l2: mov [eax],ebx             ;Store CPU number

release:
    mov ebx,[CPUnumber]
    lea ebx,[ebx+0x80000000]
    cmp [eax],ebx             ;Is lock acquired, and is CPU same?
    jne .bad                  ; no, either not acquired or wrong CPU
    mov [eax],0

答案 1 :(得分:1)

您的代码很好,但如果您正在寻找高性能,我建议您这样做:

  xor ecx, ecx
.loop:
  lock xchg [eax], ecx
  test ecx, ecx
  jz .loop

原因:

  • xor ecx, ecx较小,不需要文字,而现代CPU有硬连线快速注册零。
  • test ecx, ecx可能比cmp ecx, 0略小且速度快,因为您不需要加载文字,而test只是一个按位AND操作而不是减法。

P.S。我总是把锁前缀放在那里,不管是否隐含,出于可读性的原因 - 这显然表明我正在进行锁定操作。

答案 2 :(得分:-1)

您的代码很好,如果您有空间问题,可以随时尝试缩短代码。

其他答案提到了性能,并且显示了对锁如何工作的基本无知。

当尝试锁定时,有问题的核心会在其中一个引脚(LOCK)上发出一个信号,告知所有其他内核,它们的缓存,所有内存和所有总线主控设备(因为它们可以独立于核心)完成任何未完成的内存操作。当他们这样做时,他们共同提出另一个信号 - 锁定确认(LOCKA) - 它返回到原始核心并进行存储器交换。此后,LOCK信号关闭。

到达此处后,您可以查看使用xchg获取的值。如果事实证明另一个任务/线程拥有锁,则需要重新执行锁定序列。

假设计算机上任何位置最慢的总线控制设备都是33MHz PCI卡。如果它正在做某事,则需要任意数量的PCI总线时钟周期才能完成。每个周期都意味着3.3GHz CPU上有一百个等待周期。将此视为在锁定序列中保存一个或两个循环的角度。 CPU,芯片组和主板中有几条总线,有些,全部或全部都不能在任何时候激活 - 例如启动LOCK时。使用LOCKA回复最长的主动总线将决定锁完成的速度。

亲自尝试:衡量一千万个自旋锁(抓取和释放)需要多长时间。

我在自旋锁hereherehere上写了更多内容。

总线锁(自旋锁,Windows中的关键部分)的性能技巧是尽可能少地使用它们,这意味着组织数据以实现这一点。总线锁可能在非服务器计算机上完成得更快。这是因为服务器上的总线主控设备或多或少地不断运行。因此,如果您的应用程序是基于服务器的,那么节省总线锁定对于保持性能至关重要。

修改

致Peter Cordes,

你说明了

  

......它与总线控制无关,至少在CPU上是这样,至少不是   Nehalem的

来自最新的英特尔系统编程指南:

  

8.1.4 LOCK操作对内部处理器高速缓存的影响

     

对于Intel486和Pentium处理器,LOCK#信号总是如此   在LOCK操作期间在总线上断言,即使该区域为   被锁定的内存缓存在处理器中。

     

对于P6和更新的处理器系列,如果是内存区域   在LOCK操作期间被锁定被缓存在处理器中   正在执行LOCK操作作为回写存储器   完全包含在缓存行中,处理器可能不会断言   总线上的LOCK#信号。相反,它将修改内存位置   在内部并允许它的缓存一致性机制来确保   操作是原子地进行的。此操作称为“缓存”   锁定。“缓存一致性机制自动阻止两个或   更多处理器缓存了相同的内存区域   同时修改该区域的数据。

在第二段中,它说

  

......处理器可能不会在总线上声明LOCK#信号。

现在,我不了解你,但至少对我不了解#34;可能不会"听起来很可疑不像"不会"或者"赢了"#34;。

我所产生的数字可能是也可能不正确 - 即使我偶尔会犯错误 - 但我要求你停止引用这个或那个权威"而是通过做工作来解决我的数字,发现错误或差异,从而弄脏你的手。我将相关的源代码包含在另一个帖子中(我还抱怨你的崇高,理论,基于扶手椅的评论)所以它不会让你永远开始。

例如,通过证明我

开始
  

过度陈述锁定无争用案件的成本......

我期待你的分析。