为什么无等待如此重要

简介:

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

无锁(完全同步)

想象一下,一个无锁算法或数据结构有一个方法,这个方法所有执行过程都需要同步。假设在单线程模式下这个方法每次调用需要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)如此重要” ?

目录
相关文章
解决办法:av_interleaved_write_frame()返回-22 错误
解决办法:av_interleaved_write_frame()返回-22 错误
217 0
|
机器学习/深度学习 算法 Java
超全!」谷歌大神 LeetCode 刷题笔记 PDF 分享
超全!」谷歌大神 LeetCode 刷题笔记 PDF 分享
1226 0
|
安全 应用服务中间件 Linux
Envoy源码分析之Dispatcher机制
# Dispatcher机制 ​ Envoy和Nginx一样都是基于事件驱动的架构,这种架构的核心就是事件循环(EventLoop)。业界目前典型的几种事件循环实现主要有Libevent、Libev、Libuv、Boost.Asio等,也可以完全基于Linux系统调用epoll来实现。Envoy选择在Libevent的基础上进行了封装,实现了自己的事件循环机制,在Envoy中被称为`Disp
1594 2
|
7天前
|
NoSQL Cloud Native Redis
Redis核心开发者的新征程:阿里云与Valkey社区的技术融合与创新
阿里云瑶池数据库团队后续将持续参与Valkey社区,如过往在Redis社区一样耕耘,为开源社区作出持续贡献。
Redis核心开发者的新征程:阿里云与Valkey社区的技术融合与创新
|
7天前
|
关系型数据库 分布式数据库 数据库
PolarDB闪电助攻,《香肠派对》百亿好友关系实现毫秒级查询
PolarDB分布式版助力《香肠派对》实现百亿好友关系20万QPS的毫秒级查询。
PolarDB闪电助攻,《香肠派对》百亿好友关系实现毫秒级查询
|
8天前
|
消息中间件 Cloud Native Serverless
RocketMQ 事件驱动:云时代的事件驱动有啥不同?
本文深入探讨了云时代 EDA 的新内涵及它在云时代再次流行的主要驱动力,包括技术驱动力和商业驱动力,随后重点介绍了 RocketMQ 5.0 推出的子产品 EventBridge,并通过几个云时代事件驱动的典型案例,进一步叙述了云时代事件驱动的常见场景和最佳实践。
115082 1
|
9天前
|
弹性计算 安全 API
访问控制(RAM)|云上安全使用AccessKey的最佳实践
集中管控AK/SK的生命周期,可以极大降低AK/SK管理和使用成本,同时通过加密和轮转的方式,保证AK/SK的安全使用,本次分享为您介绍产品原理,以及具体的使用步骤。
101858 3
|
8天前
|
自然语言处理 Cloud Native Serverless
通义灵码牵手阿里云函数计算 FC ,打造智能编码新体验
近日,通义灵码正式进驻函数计算 FC WebIDE,让使用函数计算产品的开发者在其熟悉的云端集成开发环境中,无需再次登录即可使用通义灵码的智能编程能力,实现开发效率与代码质量的双重提升。
95430 2
|
5天前
|
物联网 PyTorch 测试技术
手把手教你捏一个自己的Agent
Modelscope AgentFabric是一个基于ModelScope-Agent的交互式智能体应用,用于方便地创建针对各种现实应用量身定制智能体,目前已经在生产级别落地。