为什么无等待如此重要

原文地址 ,译文链接,译者:张军,校对:梁海舰

无锁(完全同步)

想象一下,一个无锁算法或数据结构有一个方法,这个方法所有执行过程都需要同步。假设在单线程模式下这个方法每次调用需要10ns,而每增加一个线程调用这个方法,将在同步状态(变量)上产生争用。
很显然,如果只有一个线程,每次调用该方法到返回平均时间都为10ns,我们称之为:每10ns完成的任务数;如果你增加更多的线程,他们将会增加其他线程执行完成的时间,这是因为同一时刻只有一个线程能获得锁;
记住,无锁(Lock-Free)保证一个线程可以执行,但没说是哪一个,并且在当前这个特定的例子中,每次只有一个线程可以执行(完成任务),其他线程不得不从头开始:


上图中,W表示该线程赢得锁,L表示没有获得锁,意味着要重新获得;

无锁(少量同步)

上面的示意图并不能准确地描述大多数现存的无锁(Lock-Free)算法,大多数无锁算法不会将所有的时间都花在同步上,相反地,大部分时间通常花在跟算法自身相关的计算上,而这些计算并不需要重新计算当其他线程赢得运行权时,而实际上只有一部分时间是在做同步;
因此,让我们想象这样一种情况,方法需要10ns完成任务,但只有前2.5ns用来做同步;不难发现,平均而言每10ns我们可以有多个任务同时在多个线程/核上运行,准确地说,多达4个线程可以同时运行,如图所示:

上述例子中,同步时间占了该方法运行时间10ns的四分之一;
此外,如果用一个图来表示随着线程数量的增加该算法的整体性能时,我们将会看到几乎是线性的扩展到4个线程,然后遇到瓶颈无法继续升高;如果线程数进一步增加,性能甚至降低:

注意我们假设至少有4核,能同时运行4个或更多线程;

Wait-Free-Population-Oblivious(集居数无关无等待)

现在考虑一种不同的情况,有一个集居数无关无等待(Wait-Free-Population-Oblivious,简称WFPO)算法,我们假设该算法比前一个无锁(Lock-Free)算法慢3倍,即它需要30ns完成单个任务,但它是WFPO,依WFPO定义可知,只要你有足够多的核就能保证所有线程正在进展,因为它是集居数无关无等待(WFPO),不单单是无等待(Wait-Free);由同步产生的额外指令数不依赖于并发线程数,因此,仍能保持(或多或少)恒定的线程数增长;
示意图如下:

该算法随着线程数增长的总体性能表现如下图所示:

注意,性能能不断扩展直到所有的核都在运行线程,或者使用的同步原语由于一些硬件限制导致算法不再是集居数无关无等待(WFPO);

算法优化

再回到前面无锁(Lock-Free)的场景,同步花了1ns,假设有个聪明的家伙优化了计算时间,将原来的7.5ns优化为只需2.5ns,这意味着单线程模式下该算法的性能提高了一倍,那么在多线程模式下能获得多大的性能提升呢?
答案出乎意料,0。为什么是0,原因是线程只会在无竞争时运行,所以即使剩余的代码运行再快,每个线程仍然需要跟其他线程竞争且只有一个能获得锁并且执行,其他线程不得不重新竞争:

对于集居数无关无等待(WFPO)算法来说,如果你能设法将花在计算上的时间减少5ns,总体性能将得到17%(5 ns / 30 ns)左右的提升;
现在你会说:好吧,如果我们能将无锁(Lock-Free)算法花在同步上的时间从2.5ns优化为1ns该多好?
是的,你可以这么做,但你可能需要重新设计整个同步部分,这等于说你将设计一个新的无锁(Lock-Free)协同机制,它本身就是一个可发表的成果,但并不是一件容易的事(至少可以这么说),只有像Maurice Herlihy, Nir Shavit, or Doug Lea那样的人有能力做这种事,你能胜任吗?

无等待

常规无等待(Wait-Free)(非集居数无关)如何呢?
嗯,这取决于:如果随着线程数增加,操作数的增长非常平缓,那么性能可能会扩展和增加。
如果随着线程数增加,操作数成二次方地增长,那你就惨了,该算法不会比一个无锁(Lock-Free)算法好很多(甚至可能更差)。
基本上,非集居数无关无等待算法和数据结构处于无锁(Lock-Free)和集居数无关无等待(WFPO)之间。

结论

无锁(Lock-Free)算法和数据结构固有的可扩展性上限,在某个点,无论使用多少线程/核都不会提高整体性能,由于高争用实际上可能还会降低性能。
在一定程度上增加更多线程执行无锁(Lock-Free)算法是好事,你会惊讶的是对于某些算法和数据结构这个限制点有多低。如果你有长远考虑,或者你的应用打算在大量线程/核上运行,还是选择集居数无关无等待(WFPO)算法吧,否则你不会走太远。
总结一句,忘了无锁(Lock-Free)和常规无等待(Wait-Free),如果你想设计一个算法可扩展到大量的并发线程,你需要的是集居无关无等待(WFPO)。
其实,这篇文章的标题应该是:“为什么集居数无关无等待(Wait-Free-Population-Oblivious)如此重要” ?

时间: 2024-07-28 15:34:37

为什么无等待如此重要的相关文章

无等待是不够的

原文地址 ,译文链接,译者:许巧辉,校对:周可人 无等待是不够的 以目前针对演进条件(progress condition,注:参考[1])的算法和数据结构的分类来看,是不足以区分不同演进条件的能力和每一个演进条件的性能/延迟. 我们提出了一种新的"大O"记号来表示无等待的算法和数据结构.下面是它的工作原理: 一个算法或功能被称为"Wait-Free O(x)",表示需要最多O(x)操作完成. 一些示例: a)"Wait-Free O(NThreads)&

无锁和无等待的定义和例子

原文链接,译文连接,译者:周可人,校对:梁海舰 在查阅google之后,我发现没有一处对并发算法或是数据结构规定的演进条件(progress condition,注:参考[1],译者认为翻译为演进状态更为合适)做合理的解释.甚至在"The Art of Multiprocessor Programming"中也只有围绕书本的一小段定义,大部分定义是单行的句子,因而造成了我们普通人含义模糊的理解,所以在这里我把我对这些概念的理解整理在一起,并且在每一种概念后面给出相应的例子. 我们先将演

无锁并发和无等待并发的对比分析

原文地址:作者:rethinkdb  译者:sooerr 校对:方腾飞 有两种非阻塞线程同步算法,即无锁和无等待,这两种算法经常会产生混淆. 在无锁系统中,当任何特定的运算被阻塞的时候,所有CPU可以继续处理其他的运算.换种方式说,在无锁系统中,当给定线程被其他线程阻塞的时候,所有CPU可以不停的继续处理其他工作.无锁算法大大增加系统整体的吞吐量,因为它只偶尔会增加一定的交易延迟.大部分高端数据库系统是基于无锁算法而构造的,以满足不同级别. 相反,无等待算法保证了所有CPU在连续处理有效工作的时

【翻译】软文:ZooKeeper:无等待的互联网扩容协调系统

翻译自:Paper: ZooKeeper: Wait-Free Coordination For Internet-Scale Systems 你依然需要自己滚动么?ZooKeeper: Wait-free coordination for Internet-scale systems: 在这篇文章里,我描述ZooKeeper,一个协调分布式系统进程的服务.当ZooKeeper是临界基础设施的一部分,ZooKeeper目标在客户端提供一个简单高效的内核建立一个更复杂的协调原语.它包含群信息的基础

如何实现115网盘看视频无等待

  工具/原料 火狐浏览器; 云共撸软件; 火狐浏览器安装greasemonkey插件 1首先需要在计算机上安装火狐浏览器(如果希望快速安装可以下载火狐浏览器离线安装包进行安装),安装完成后启动火狐浏览器,在火狐浏览器右上角处单击"菜单"按钮,在打开的下拉菜单中选择"附件组件",在打开的附件组件页面的搜索框中输入"greasemonkey"(该插件也称为油猴子),在搜索结果中选择greasemonkey3.0单击"安装"; 2

无锁并不像人们所说的那么好

原文链接,译文连接,译者:梁海舰,校对:李任 注意,我将在这篇文章中说一些关于"无锁"和"无等待"算法的坏话.在我们开始之前,让我来问你一个问题: 你之前有过需要无锁算法来解决问题吗?为什么必须使用无锁算法来解决? 这是一个意味深长的问题,在某种意义上,我们尝试区分哪些问题需要"无锁"或者受益于"无锁"提供的保证,哪些问题使用阻塞解决方案就已经足够了.那么,什么样的问题可以利用无锁算法解决呢? 你会说高竞争下的扩展性?是的,听

Java并发中正确使用volatile

作者:一粟   整理和翻译自Twitter实时搜索的PPT 前几天并发编程群里有同学对volatile的用法提出了疑问,刚好我记得Twitter有关实时搜索的这个PPT对这个问题解释的很清晰并有一个实际的应用场景,于是周末把这个问题摘录了一些和并发相关的内容如下: 并发 – 定义 悲观锁 – Pressimistic locking 一个线性在执行一个操作时持有对一个资源的独占锁.(互斥) 一般用在冲突比较可能发生的场景下 乐观锁 – Optimistic locking 尝试采用原子操作,而不

异步调用Web服务方法

基于Ajax技术构建的门户是web 2.0这一代中最为成功的Web应用程序.而这块市场上iGoogle和Pageflakes这两大站点已经走在了时代的前列. 当你打开Pageflakes,将会看到如下的界面: 接下来就是界面上的各个"部件"去向服务器请求各种web服务,而服务器作为代理,则代为向外部web服务发出请求.(这是因为ajax调用无法跨越,所以常通过代理来请求数据) 问题场景:某个很受用户欢迎的"部件"很长时间不能执行,导致很对请求无法及时执行,引起请求失

从零开始学.net多线程系列(三)——同步

本文将涉及如下内容 Wait Handles EventWaitHandle Seamphores Mutex Critical Sections Miscellaneous Objects 这篇文章重点说明多个不同的线程之间的同步问题. WaitHandles 首先,我们必须认识到,当你尝试着理解怎么才能使多个线程在一起协调地很好,最关键的问题是怎样排序这些操作.例如,我们有如下的这些问题: 1. 我们需要创建一个订单 2. 我们需要保存订单,但是除非我们获得了订单号,否则我们无法进行保存操作