对于WORD,cmpxchg比BYTE快

昨天我发布了关于如何编写快速螺旋锁的问题。 感谢Cory Nelson,我似乎找到了一种超越我的问题中讨论的其他方法的方法。 我使用CMPXCHG指令来检查锁是否为0,因此是空闲的。 CMPXCHG使用'BYTE', WORDDWORD 。 我认为该指令在BYTE上运行得更快。 但是我写了一个实现每个数据类型的锁:

inline void spin_lock_8(char* lck)
{
    __asm
    {
        mov ebx, lck                        ;move lck pointer into ebx
        xor cl, cl                          ;set CL to 0
        inc cl                              ;increment CL to 1
        pause                               ;
        spin_loop:
        xor al, al                          ;set AL to 0
        lock cmpxchg byte ptr [ebx], cl     ;compare AL to CL. If equal ZF is set and CL is loaded into address pointed to by ebx
        jnz spin_loop                       ;jump to spin_loop if ZF
    }
}
inline void spin_lock_16(short* lck)
{
    __asm
    {
        mov ebx, lck
        xor cx, cx
        inc cx
        pause
        spin_loop:
        xor ax, ax
        lock cmpxchg word ptr [ebx], cx
        jnz spin_loop
    }
}
inline void spin_lock_32(int* lck)
{
    __asm
    {
        mov ebx, lck
        xor ecx, ecx
        inc ecx
        pause
        spin_loop:
        xor eax, eax
        lock cmpxchg dword ptr [ebx], ecx
        jnz spin_loop
    }
}
inline spin_unlock(<anyType>* lck)
{
    __asm
    {
        mov ebx, lck
        mov <byte/word/dword> ptr [ebx], 0
    }
}

然后使用下面的伪代码测试该锁(请注意,lcm-pointer总是指向可被4除的地址):

<int/short/char>* lck;
threadFunc()
{
    loop 10,000,000 times
    {
        spin_lock_8/16/32 (lck);
        spin_unlock(lck);
    }
}
main()
{
    lck = (char/short/int*)_aligned_malloc(4, 4);//Ensures memory alignment
    start 1 thread running threadFunc and measure time;
    start 2 threads running threadFunc and measure time;
    start 4 threads running threadFunc and measure time;
    _aligned_free(lck);
}

在具有2个能够运行4个线程的处理器(Ivy Bridge)的处理器上,我已经获得了以msecs度量的以下结果。

           1 thread    2 threads     4 threads
8-bit      200         700           3200
16-bit     200         500           1400
32-bit     200         900           3400

数据表明,所有功能都需要等量的时间来执行。 但是,当多线程必须检查使用16位的lck == 0是否会显着更快。 这是为什么? 我不认为这与lck的对齐有关系吗?

提前致谢。


从我记得的锁在一个单词(2字节)上工作。 它在486年首次推出时就是这样写的。

如果你携带一个不同大小的锁,它实际上会产生相当于2个锁(锁定字A和字B用于双字)。对于一个字节,它可能必须防止锁定第二个字节,这有点类似到2个锁...

所以你的结果与CPU优化一致。


想象一下,有1234个线程和16个CPU。 一个线程获取自旋锁,然后操作系统执行任务切换。 现在你有16个CPU,每个都运行剩下的1233个线程中的一个线程,所有线程都以非常毫无意义的方式旋转,不管怎样,操作系统将CPU时间返回到唯一可以释放螺旋锁的线程需要很长时间。 这意味着整个操作系统基本上都可以锁定(所有的CPU都会停止工作)几秒钟。 这严重迟钝; 那么你如何解决它?

您通过在用户空间中不使用螺旋锁来修复它。 只有在任何开关可以被禁用的情况下才能使用自旋锁。 只有内核应该能够禁用任务切换。

更具体地说,你需要使用互斥锁。 现在,互斥体可能会在放弃并让线程等待锁之前旋转,而且(对于典型/低争用情况)这确实有所帮助,但它仍然是一个互斥锁,并不是自旋锁。

下一个; 对于理智的软件来说,重要的是(为了性能)避免锁争用,然后确保无争用的情况很快(如果没有争用,好的互斥体不会导致任务切换)。 您正在衡量相关/不相关的案例。

最后; 你的锁不好。 为避免过多使用lock前缀,您应该测试您是否可以在没有任何lock前缀的情况下获取,并且只有当您可以使用lock前缀时才能获取lock前缀。 英特尔(可能还有很多其他人)称这种策略为“测试;然后(测试和设置)”。 此外,您还没有理解pause的目的(或者对于那些不支持10年前指令的汇编程序来说是“rep nop”)。

一个体面的自旋锁可能看起来像这样:

acquire:
    lock bts dword [myLock],0   ;Optimistically attempt to acquire
    jnc .acquired               ;It was acquired!
.retry:
    pause
    cmp dword [myLock],0        ;Should we attempt to acquire again?
    jne .retry                  ; no, don't use `lock`
    lock bts dword [myLock],0   ;Attempt to acquire
    jc .retry                   ;It wasn't acquired, so go back to waiting
.acquired:
    ret

release:
    mov dword [myLock],0        ;No lock prefix needed here as "myLock" is aligned
    ret

另外请注意,如果您未能充分减少锁定争用的可能性,那么您确实需要关心“公平性”,而不应该使用自旋锁。 “不公平的”螺旋锁的问题在于某些任务可能是幸运的,并且总是获得锁定,并且一些任务可能不吉利,并且从未获得锁定,因为幸运任务总是获得锁定。 这一直是严重竞争锁定的问题,但对于现代NUMA系统来说,这成为更可能的问题。 在这种情况下,您至少应该使用车票锁。

门票锁的基本思想是确保任务按照他们到达的顺序获取锁(而不是一些“可能非常糟糕”的随机顺序)。 为了完整起见,票证锁定可能如下所示:

acquire:
    mov eax,1
    lock xadd [myLock],eax           ;myTicket = currentTicket, currentTicket++

    cmp [myLock+4],eax               ;Is it my turn?
    je .acquired                     ; yes
.retry:
    pause
    cmp [myLock+4],eax               ;Is it my turn?
    jne .retry                       ; no, wait
.acquired:
    ret

release:
    lock inc dword [myLock+4]
    ret

TL;博士; 开始时,您不应该使用错误的工具(自旋锁)。 但如果你坚持使用错误的工具,那么至少得到正确实施的错误工具...... :-)

链接地址: http://www.djcxy.com/p/11089.html

上一篇: cmpxchg for WORD faster than for BYTE

下一篇: getApplication in BroadcastReceiver in android