C++ pthread_spinlock和boost::smart_ptr::spinlock之间的区别?

C++ pthread_spinlock和boost::smart_ptr::spinlock之间的区别?,c++,linux,performance,spinlock,C++,Linux,Performance,Spinlock,我在boost::smart\u ptr中发现了以下自旋锁代码: bool try_lock() { return (__sync_lock_test_and_set(&v_, 1) == 0); } void lock() { for (unsigned k=0; !try_lock(); ++k) { if (k<4) ; // spin else if (k < 16)

我在
boost::smart\u ptr
中发现了以下自旋锁代码:

bool try_lock()
{
    return (__sync_lock_test_and_set(&v_, 1) == 0);
}
void lock()
{    
    for (unsigned k=0; !try_lock(); ++k)
    {
        if (k<4)
            ; // spin
        else if (k < 16)
            __asm__ __volatile__("pause"); // was ("rep; nop" ::: "memory")
        else if (k < 32 || k & 1)
            sched_yield();
        else
        {
            struct timespec rqtp;
            rqtp.tv_sec  = 0;
            rqtp.tv_nsec = 100;
            nanosleep(&rqtp, 0);
        }
    }
}
void unlock()
{
     __sync_lock_release(&v_);
}
我承认我对组装的理解不是很好,所以我不完全理解这里发生了什么。(有人能解释一下这是怎么回事吗?)

但是,我对boost自旋锁和glibc pthread_自旋锁运行了一些测试,当内核数大于线程数时,boost代码的性能优于glibc代码

另一方面,当线程比内核多时,glibc代码更好


为什么会这样?这两个spinlock实现之间的区别是什么,使得它们在每个场景中的性能不同?

问题中发布的
pthread\u spin\u lock()
实现在哪里?它似乎遗漏了几条重要的线路

我看到的实现(它不是内联程序集-它是来自
glibc/nptl/sysdeps/i386/pthread\u spin\u lock.s
的独立程序集源文件)看起来很相似,但有两个额外的关键指令:

#include <lowlevellock.h>

    .globl  pthread_spin_lock
    .type   pthread_spin_lock,@function
    .align  16
pthread_spin_lock:
    mov 4(%esp), %eax
1:  LOCK
    decl    0(%eax)
    jne 2f
    xor %eax, %eax
    ret

    .align  16
2:  rep
    nop
    cmpl    $0, 0(%eax)
    jg  1b
    jmp 2b
    .size   pthread_spin_lock,.-pthread_spin_lock
#包括
.globl pthread_spin_lock
.type pthread_spin_lock,@function
.对齐16
pthread_spin_lock:
mov 4(%esp),%eax
1:锁
10月0日(%eax)
jne 2f
异或%eax,%eax
ret
.对齐16
2:代表
不
cmpl$0,0(%eax)
jg 1b
jmp-2b
.size pthread\u spin\u lock、-pthread\u spin\u lock
它递减传入参数所指向的
long
,如果结果为零则返回

否则,结果为非零,这意味着该线程没有获得锁。因此,它执行一个
rep nop
,相当于
pause
指令。这是一个“特殊”nop,它向CPU提示线程处于旋转状态,CPU应以某种方式处理内存排序和/或分支预测,以提高这些情况下的性能(我并不假装完全理解芯片外壳下发生的不同情况——从软件的角度来看,与普通的老
nop
)没有区别

暂停
之后,它会再次检查该值-如果该值大于零,则锁将无人认领,因此它会跳到函数顶部并再次尝试认领锁。否则,它会再次跳到
暂停


这个自旋锁和Boost版本的主要区别在于,当它旋转时,它从来没有做过比
暂停
更有趣的事情-没有什么比
调度产量()或
纳米睡眠()
。因此线程保持热状态。我不确定这在您注意到的两种行为中是如何起作用的,但glibc代码将更加贪婪-如果一个线程在锁上旋转,并且有其他线程准备运行,但没有可用的内核,旋转的线程不会帮助等待的线程获得任何cpu时间,而Boost版本将不会ally自愿为等待关注的线程让路。

有趣的是,几年前我做了一个类似的测试,得出了相同的结论:
pthread\u spin\u lock
比手动spinlock(boost中的那一行)更有效当有很多争用的时候。我在网上的某个地方找到了它-不幸的是,我记不起确切的位置-我在测试中使用了pthread_spin_锁,找到了我报告的结果,当我看到差异时,就去寻找源代码,试图理解发生了什么,发现了程序集,当我不理解时,它就来了在这里寻求帮助。感谢您的回复和解释!我想知道这对我来说似乎有悖常理-greedier pthread实现不会在争用下放弃其核心,当核心被超额订阅时(线程数多于核心数)性能会更好-我本以为boost实现在这种情况下会做得更好。@lori:很难说到底发生了什么-我们没有关于基准测试的信息。也就是说,我不确定在用户模式代码中使用自旋锁的频率有多高。即使有时会这样做,我认为它们也应该只在期望低争用的情况下使用d.他们会尽可能简短地举行会议。执行Boost所做的检查可能有点过度工程化。再说一遍,这只是基于直觉的观点,而不是数据。
#include <lowlevellock.h>

    .globl  pthread_spin_lock
    .type   pthread_spin_lock,@function
    .align  16
pthread_spin_lock:
    mov 4(%esp), %eax
1:  LOCK
    decl    0(%eax)
    jne 2f
    xor %eax, %eax
    ret

    .align  16
2:  rep
    nop
    cmpl    $0, 0(%eax)
    jg  1b
    jmp 2b
    .size   pthread_spin_lock,.-pthread_spin_lock