在哪里可以找到“自适应”pthread互斥锁的文档?在我的系统上定义了PTHREAD_MUTEX_ADAPTIVE_NP符号,但是我可以在网上找到的only documentation没有说明什么是自适应互斥,或者什么时候适合使用它。
那么...它是什么,我应该什么时候使用它?
作为参考,我的libc版本是:
GNU C Library (Ubuntu EGLIBC 2.15-0ubuntu10.5) stable release version 2.15, by Roland McGrath et al.
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.6.3.
Compiled on a Linux 3.2.50 system on 2013-09-30.
Available extensions:
crypt add-on version 2.1 by Michael Glad and others
GNU Libidn by Simon Josefsson
Native POSIX Threads Library by Ulrich Drepper et al
BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.debian.org/Bugs/>.
而“uname -a”给出了
Linux desktop 3.2.0-55-generic #85-Ubuntu SMP Wed Oct 2 12:29:27 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
3条答案
按热度按时间jjhzyzn01#
PTHREAD_MUTEX_ADAPTIVE_NP
是我作为一个glibc贡献者在工作时发明的,目的是使LinuxThreads更可靠,性能更好。LinuxThreads是glibc的NPTL library的前身,最初由Xavier Leroy开发,他也是著名的OCaml的创建者之一。自适应互斥体以基本上未修改的形式存活到NTPL中:代码几乎是相同的,包括用于估计器平滑的魔常数和相对于估计器的最大自旋。
在SMP下,当您去获取一个互斥体并看到它被锁定时,简单地给予并调用内核进行阻塞可能是次优的。如果锁的所有者只持有几条指令的锁,那么只等待这些指令的执行,然后用原子操作获取锁,而不是通过进行系统调用花费数百个额外的周期,这会更便宜。
内核开发人员非常了解这一点,这也是为什么我们在Linux内核中为快速临界区设置自旋锁的原因之一。(当然,其他原因之一是,由于处于中断上下文中而不能休眠的代码可以获取自旋锁。
问题是,你应该等多久?如果您一直旋转直到获取锁,那可能是次优的。用户空间程序不像内核代码那样写得很好(咳嗽)。它们可能有很长的临界区。它们也不能禁止优先购买权;有时关键部分由于上下文切换而爆炸。(POSIX线程现在提供了真实的时间工具来处理这个问题:您可以将线程放入实时优先级和FIFO调度等,再加上配置处理器亲和性。)
我想我们尝试了固定的迭代次数,但后来我有了这个想法:既然我们可以测量,为什么还要猜测呢?为什么我们不实现锁持续时间的平滑估计器,类似于我们为TCP重传超时(RTO)估计器所做的。每次旋转一个锁时,我们都应该测量它实际需要多少次旋转才能获得它。此外,我们不应该永远旋转:我们也许应该只旋转当前估计器值的至多两倍。当我们进行测量时,我们可以指数平滑它,只需几个指令:取前一个值和新值的一小部分,并将它们加在一起,这与将它们的差的一小部分加回估计量相同:比如
estimator += (new_val - estimator)/8
,表示新旧值之间的1/8到7/8混合。你可以把它想象成一只看门狗。假设估计器告诉您,锁平均需要80次旋转才能获得。那么,你可以很自信地说,如果你已经执行了160次旋转,那么有些事情是错误的:锁的所有者正在执行一些异常长的情况,或者可能遇到了页面错误或被抢占。此时,等待线程减少了损失,并调用内核进行阻塞。
没有测量,你就无法准确地做到这一点:没有“一刀切”的价值观。比如说,200次旋转的固定限制在一个程序中是次优的,这个程序的临界区非常短,几乎总是在等待10次旋转后就可以获取锁。每当出现异常等待时间时,互斥锁函数将消耗200次迭代,而不是在20次时很好地放弃并节省周期。
这种自适应方法是专用的,因为它不会对所有程序中的所有锁都起作用,所以它被打包为一种特殊的互斥类型。例如,对于长时间锁定互斥锁的程序,它就不能很好地工作:周期如此之长,以至于与进入内核相比,更多的CPU时间被浪费在大的估计值上。这种方法也不适合单处理器:除了试图获得锁的线程之外的所有线程都在内核中被挂起。这种方法也不适用于公平性很重要的情况:它是一个机会锁。不管有多少其他线程已经等待了多久,也不管它们的优先级是什么,一个新线程都会沿着而来并夺取锁。
如果您有行为良好的代码,其中包含高度争用的短临界区,并且您希望在SMP上获得更好的性能,那么自适应互斥体可能值得一试。
1.这些工作可以在Git历史中找到。更新
linuxthreads/ChangeLog
文件的提交是here;引用的实际更改是here与另一个作者对charmap.c
文件的不相关更改混合在一起。eivgtgni2#
该符号在那里提到:
http://elias.rhi.hi.is/libc/Mutexes.html
“LinuxThreads只支持一个互斥属性:互斥体类型,即PTHREAD_MUTEX_ADAPTIVE_NP(“快速”互斥体)、PTHREAD_MUTEX_RECURSIVE_NP(“递归”互斥体)、PTHREAD_MUTEX_TIMED_NP(“定时”互斥体)或PTHREAD_MUTEX_RECURSIVE_NP(“错误检查”互斥体)。正如NP后缀所表明的,这是对POSIX标准的不可移植的扩展,不应该在可移植程序中使用。
互斥量类型决定了当一个线程试图用pthread_mutex_lock锁定它已经拥有的互斥量时会发生什么。如果互斥量是“fast”类型,pthread_mutex_lock只会永久挂起调用线程。如果互斥体是“错误检查”类型,pthread_mutex_lock会立即返回错误代码EDEADLK。如果互斥体是“recursive”类型的,那么对pthread_mutex_lock的调用会立即返回一个成功返回代码。拥有互斥体的线程锁定互斥体的次数记录在互斥体中。拥有线程必须调用pthread_mutex_unlock相同的次数,互斥体才能返回到unlocked状态。
默认互斥锁类型为“timed”,即PTHREAD_MUTEX_TIMED_NP。
编辑:更新了jthill发现的信息(谢谢!)
关于互斥标志和PTHREAD_MUTEX_ADAPTIVE_NP的更多信息可以在here中找到:
PTHRED_MUTEX_ADAPTIVE_NP是一个新的互斥体,旨在以牺牲公平性和CPU周期为代价实现高吞吐量。这个互斥体不会将所有权转移给等待线程,而是允许竞争。此外,在SMP内核上,锁操作使用旋转来重试锁,以避免立即取消锁定的成本。
基本上是这样的:在需要高吞吐量的情况下,由于其本身的性质,可以实现需要线程逻辑额外考虑的互斥。您将不得不设计一种算法,可以使用这些属性,从而实现高吞吐量。在执行顺序不重要的情况下,从内部(而不是“从内核”)进行负载平衡的东西。
有一本关于Linux/unix多线程编程的好书,名字我忘了。如果我找到了,我会更新的。
flvlnr443#
Here you go。正如我所读到的,这是一个极其简单的互斥体,除了使无争用情况快速运行之外,它不关心任何事情。