pthread_spinlock 和 Boost::smart_ptr::Spinlock 之间的区别
difference between pthread_spinlock and boost::smart_ptr::spinlock?
我在boost::smart_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_);
}
因此,如果我理解正确,当锁被争用时,传入的线程将呈指数级退避,首先疯狂旋转,然后暂停,然后产生剩余的时间片,最后在睡眠和屈服之间翻转。
我还找到了glibc pthread_spinlock
实现,它使用汇编来执行锁定。
#define LOCK_PREFIX "lock;" // using an SMP machine
int pthread_spin_lock(pthread_spinlock_t *lock)
{
__asm__ ("n"
"1:t" LOCK_PREFIX "decl %0nt"
"jne 2fnt"
".subsection 1nt"
".align 16n"
"2:trep; nopnt"
"cmpl $0, %0nt"
"jg 1bnt"
"jmp 2bnt"
".previous"
: "=m" (*lock)
: "m" (*lock));
return 0;
}
我承认我对组装的理解不是很好,所以我不完全理解这里发生了什么。(有人可以解释一下这是在做什么吗?
但是,我针对 boost spinlock 和 glibc pthread_spinlock运行了一些测试,当内核多于线程时,boost 代码的性能优于 glibc 代码。
另一方面,当线程多于内核时,glibc 代码更好。
这是为什么呢?这两种自旋锁实现之间有什么区别,使它们在每种情况下的性能不同?
你从哪里得到问题中发布的pthread_spin_lock()
实现?它似乎缺少几行重要行。
我看到的实现(不是内联程序集 - 它是来自glibc/nptl/sysdeps/i386/pthread_spin_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
它递减传入参数指向的long
,如果结果为零,则返回。
否则,结果为非零,这意味着此线程未获取锁。所以它执行一个rep nop
,相当于pause
指令。 这是一个"特殊"的nop,它向CPU提示线程正在旋转,并且CPU应该以某种方式处理内存排序和/或分支练习,以提高这些情况下的性能(我不假装确切地理解芯片下发生了什么不同 - 从软件的角度来看, 与普通的旧nop
没有区别)。
pause
后,它会再次检查该值 - 如果它大于零,则锁无人认领,因此它会跳到函数的顶部并尝试再次声明锁。否则,它会再次跳转到pause
。
这个旋转锁和 Boost 版本之间的主要区别在于,这个旋转时永远不会做比pause
更花哨的事情 - 没有什么比sched_yield()
或nanosleep()
更棒的了。 所以线程保持热度。 我不确定这在您注意到的两种行为中是如何发挥作用的,但 glibc 代码会更贪婪 - 如果一个线程在锁上旋转并且有其他线程准备运行但没有可用的内核,旋转线程不会帮助等待线程获得任何 CPU 时间,而 Boost 版本最终会自愿为等待一些关注的线程让路。
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- int(c) 和 c-'0' 之间的区别。C++
- 在cuda线程之间共享大量常量数据
- 在c代码之间共享数据的最佳方式
- Mix_Init和Mix_OpenAudio SDL之间的区别是什么
- C++ 使用 assign 函数的字符串与直接使用 '=' 更改值的字符串之间的区别
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- std::atomic和std::condition_variable wait,notify_*方法之间的区别
- 大小相等但成员数量不同的结构之间的性能差异
- 类与私有变量的其他类之间的线程安全性
- 如何在cpp文件之间切换窗口?在Qt中
- 线程之间的布尔停止信号
- 我是C++编程的新手,这些代码之间有什么区别,我应该使用哪一个
- 在 const 函数中通过引用和指针返回之间的区别
- 我想知道长双倍和双倍之间的区别
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 使用.find函数在c++中查找字符和另一个字符之间的大小
- 构造函数和转换运算符之间的重载解析
- pthread_spinlock 和 Boost::smart_ptr::Spinlock 之间的区别