并发数据结构- 1.1.1 性能

原文链接译文链接,译者:俞升兵,校对:周可人

1.1.1 性能

一个运行在P个处理上的应用程序的加速度是它在单个处理器上的执行时间和在P个处理器的执行时间的比值。这是一种评价应用程序对于机器资源利用程度的衡量。理想情况下,我们想要的结果是线性加速度:当我们使用P个处理器的时候,我们希望可以获得P的加速度(译者注:例如一个应用程序在单处理器的执行时间是10秒,那么在双处理的执行时间理想情况下是5秒)。加速度随着P一起增加的数据结构我们称之为可扩展的数据结构。在设计可扩展的数据结构的时候,我们必须注意:为了同步使用简单的方法会严重破坏扩展性。

回到基于锁的计数器,我们会发现锁会带来顺序性的瓶颈:在任何时间点,最多只有一个fetch-and-inc操作是有用的,例如:递增变量X。这种顺序性的瓶颈会对本来能够到达的加速度造成令人惊讶的影响。顺序执行部分代码对性能带来的影响已经在基于Amdahl’s law的一个简单公式中阐明了。假设b是一个程序中顺序性瓶颈所占的百分比。假设在单处理器中运行这个程序需要1个时间单位,那么在P个处理器的环境中,执行顺序运行部分的代码需要b个时间单位,同时在最好的情况下,并发部分的代码消耗(1-b)/p个时间单位,那么加速度最多等于 1/(b+(1-b)/p) 。这意味中如果程序中只有10%是属于顺序性瓶颈的部分,那么在一个10个处理器的环境中,程序能达到的加速度最多只有5.3:我们的应用只利用了机器的一半性能。减少顺序性执行代码块的数量和长度对性能是至关重要的。在基于锁的上下文中,这意味着减少申请锁的次数,降低锁的粒度:一种用来表示在持有锁时,执行指令数目的衡量。

我们的简单计数器实现碰到的第二个问题是内存竞争:这是底层硬件为了多线程并发尝试访问相同的内存地址所造成的流量的开销。只有当你理解普通的共享内存多处理器架构一些方面,你才能意识到内存竞争。如果保护着我们计数器的锁就像很多简单锁一样,是使用单一地址实现的话,那么为了请求锁,线程必须重复尝试修改这个地址。以缓存一致性多处理器为例,包含着锁的cache line的独占所有权必须重复的从一个处理器传输到别的处理器上,这会导致每次尝试修改内存地址的操作都需要长时间的等待,失败的尝试获取锁的操作会进一步加重相应的内存流量。在1.1.7中我们会讨论针对不同类型的共享内存架构实现相应的锁,避免类似这样的问题.

基于锁的实现的计数器的第三个问题是:当持有锁的线程被延期了,那么所有尝试获取锁的线程都会被延期。这种现象称为阻塞,阻塞是多道程序设计(multiprogrammed)中特有的问题,每个处理器都有多个线程,操作系统会给拥有锁的线程优先权。对于很多的数据结构来说,这个问题可以通过设计非阻塞的算法来解决,在非阻塞算法中一个线程的延期不会导致其他线程的延期。根据定义而言,这些算法不能使用锁。

下面我们继续讨论我们的共享计数器的例子,分别讨论阻塞和非阻塞技术;我们会引入更多和性能相关的话题. 

时间: 2024-10-31 05:20:11

并发数据结构- 1.1.1 性能的相关文章

并发数据结构-1.1 并发的数据结构的设计

原文链接,译文链接,译者:董明鑫,校对:周可人 随着多个处理器共享同一内存的机器在商业上的广泛使用,并发编程的艺术也产生了巨大的变化.当前的趋势向着低功耗芯片级多线程(CMT)发展,所以这样的机器一定会更加广泛的被使用. 共享内存多处理器是指并发的执行多个线程的系统,这些线程在共享的内存中通过数据结构通讯和同步.这些数据结构的效率对于性能是很关键的,而目前熟练掌握为多处理器机器设计高效数据结构这一技术的人并不多.对大多数人来说,设计并发的数据结构比设计单线程的难多了,因为并发执行的线程可能会多种

并发数据结构-1.1.4 复杂度测量&1.1.5 正确性

原文链接,译文链接,译者:张军,校对:周可人 1.1.4 复杂度测量 一个被广泛研究的方向是在理想化模型,如并行随机存取机上分析并发数据结构和算法的渐进复杂度[35, 122, 135].然而,很少有将这些数据结构放在一个真实的多处理器上进行建模的.这里有多种原因,大部分原因跟系统硬件架构与线程异步执行的相互作用有关.想想组合树(combining tree)的例子,虽然我们能通过计算(指令数)得到O(P/logP)的加速比,但这无法反映在实证研究中[52, 129].真实世界的行为是被上述其他

并发数据结构-1.1.2 阻塞技术

原文链接,译文链接,译者:周可人,校对:梁海舰 1.1.2 阻塞技术 在很多数据结构中,内存竞争所带来的不良现象和前文所说的顺序瓶颈带来的影响都可以通过使用细粒度锁机制来减小.在细粒度锁机制中,我们用多个粒度较小的锁来保护数据结构中的不同部分.这样做的目的是允许并发操作在它们不访问数据结构的相同部分时并行执行.这种方法也可以用于避免独立内存位置访问的额外竞争.在一些数据结构中,这种现象经常发生:举个例子,在哈希表中,对那些被哈希到不同哈希桶中的值的操作自然访问的是数据结构中的一部分. 对其他的数

并发数据结构-1.1.3 非阻塞技术

原文链接,译文链接,译者:Noodles,校对:周可人 1.1.3 非阻塞技术 正如前面讨论的那样,非阻塞实现主要目的是为了消除由锁带来的相关问题,为了形式化研究这一概念,多种非阻塞演进条件已经在相关文献有所研究了,如wait-freedom演进条件,lock-freedom演进条件,和obstruction-freedom演进条件.满足wait-free演进条件的操作是指在执行自身包含的有限步骤之后,保证操作必须完成,而不用考虑其他操作发生的时序,满足lock-free演进条件的操作是指在执行

招募译者翻译并发数据结构

什么是并发数据结构? 引用wiki上的定义 In computer science, a concurrent data structure is a particular way of storing and organizing data for access by multiple computing threads (or processes) on a computer. 简而言之,并发数据结构即允许多线程同时访问(读和写)的数据结构. 并发数据结构中的算法的设计原则主要分两大类,li

并发数据结构- 1.8 优先队列&1.9 总结

原文链接,译文链接,译者:郭振斌,校对:周可人 1.8 优先队列 并发的优先队列是一个可线性化到顺序优先队列的数据结构,能够通过常用的优先队列语义提供insert和delete-min操作. 基于堆的优先队列 许多文献中提到的并发优先队列结构,其实是本书前面提到的可线性化堆结构.再一次的,这种结构的基本思想是在个别堆节点上使用细粒度锁,使线程在并行下也能够尽可能的访问数据结构的不同部分.设计这种并发堆的关键问题,在于传统自底向上的insert和自顶向下的delete-min操作有可能造成死锁.B

并发数据结构-1.5 链表

原文链接,译文链接,译者:huavben,校对:周可人 考虑支持插入,删除和查找操作的并发数据结构实现.如果这些操作只处理键值(译者注:而不处理具体值),这样的数据结构会是一个集合.如果一个数据值与每一个键关联起来,我们就得到了一部数据字典.由于他们都是密切相关的数据结构,一个并发的集合通常能够经过适当修改来实现一部字典.在接下来的三个小节中,我们将专注于利用linked lists,hash tables,和trees这三种不同的数据结构来实现集合. 试想我们使用一个linked list去实

并发数据结构-1.7 查找树

原文链接,译文链接,译者:iDestiny,校对:周可人 任何查找树的并发实现都可以通过用一个独占锁保护来完成.通过使用读写锁对并发性能有一定提升,读写锁允许所有只读(查找)操作并发地执行,因为读操作是以共享模式持有锁,然而更新(插入或删除)操作持有独占模式的锁,从而排斥其他所有操作.如果更新操作比较少,这还能接受,但是只要有适量的更新操作,那么更新操作所持有的独占锁将产生线性的瓶颈,从而大大降低性能.通过使用细粒度的锁策略--比如每个节点一个锁,而不是整棵树使用同一个锁--这样我们进一步地提升

并发数据结构-1.4 池

原文链接,译文链接,译者:huavben,校对:周可人 实现高效并发栈和队列的大部分挑战来自于一个被插入的元素可以被删除这一需求.并发池是一种支持插入和删除操作的数据结构,它允许删除操作移除任何一个已经被插入的,并且没有在随后被删除的元素.这样的弱需求提供了提高并发性能的机会. 一个高效的并发池可以使用任意静态一致的计数器来构建.在这样的并发池中,元素被置于数组当中,fetch-and-inc操作决定插入操作在哪个位置存储元素,同样的,fetch-and-inc操作决定删除操作在哪个位置获得元素