专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

关于Disruptor高性能队列原理的浅析

  介绍
  Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与IO操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家MartinFowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。
  本文主要参考它2011年的论文《LMAXDisruptor:Highperformancealternativetoboundedqueuesforexchangingdatabetweenconcurrentthreads》还结合了美团技术团队对它分析的文章。论文中文翻译参考了肥兔子爱豆畜子翻译的中文版。
  这里所说的队列是系统内部的内存队列,而不是Kafka这样的分布式队列。
  许多应用程序依靠队列在处理阶段之间交换数据。我们的性能测试表明,当以这种方式使用队列时,其延迟成本与磁盘(基于RAID或SSD的磁盘系统)的IO操作成本处于同一数量级都很慢。如果在一个端到端的操作中有多个队列,这将使整个延迟增加数百微秒。
  测试表明,使用Disruptor的三阶段流水线的平均延迟比基于队列的同等方法低3个数量级。此外,在相同的配置下,Disruptor处理的吞吐量约为8倍。并发问题
  在本文以及在一般的计算机科学理论中,并发不仅意味着两个以上任务同时并行发生,而且意味着它们在访问资源时相互竞争。争用的资源可以是数据库、文件、socket,甚至是内存中的一个位置。
  代码的并发执行涉及两件事:互斥和内存可见性。互斥是关于如何管理保证某些资源的独占式使用。内存可见性是关于控制内存更改何时对其他线程可见。如果你可以避免多线程竞争的去更新共享资源,那么就可以避免互斥。如果您的算法可以保证任何给定的资源只被一个线程修改,那么互斥是不必要的。读写操作要求所有更改对其他线程可见。但是,只有争用的写操作需要对更改进行互斥。
  在任何并发环境中,最昂贵的操作是争用写访问。要让多个线程写入同一资源,需要复杂而昂贵的协调。通常,这是通过采用某种锁策略来实现的。
  但是锁的开销是非常大的,在论文中设计了一个实验:这个测试程序调用了一个函数,该函数会对一个64位的计数器循环自增5亿次。机器环境:2。4G6核运算:64位的计数器累加5亿次
  单线程情况下,不加锁的性能CAS操作的性能加锁的性能。
  在多线程情况下,为了保证线程安全,必须使用CAS或锁,这种情况下,CAS的性能超过锁的性能,前者大约是后者的8倍。
  保证线程安全一般使用锁或者原子变量。
  采取加锁的方式,默认线程会冲突,访问数据时,先加上锁再访问,访问之后再解锁。通过锁界定一个临界区,同时只有一个线程进入。
  原子变量能够保证原子性的操作,意思是某个任务在执行过程中,要么全部成功,要么全部失败回滚,恢复到执行之前的初态,不存在初态和成功之间的中间状态。例如CAS操作,要么比较并交换成功,要么比较并交换失败。由CPU保证原子性。
  通过原子变量可以实现线程安全。执行某个任务的时候,先假定不会有冲突,若不发生冲突,则直接执行成功;当发生冲突的时候,则执行失败,回滚再重新操作,直到不发生冲突。
  CAS操作是一种特殊的机器代码指令,它允许将内存中的字有条件地设置为原子操作。比如对于前面的递增计数器实验例子,每个线程都可以在一个循环中自旋,读取计数器,然后尝试以原子方式将其设置为新的递增值。
  如图所示,Thread1和Thread2都要把Entry加1。若不加锁,也不使用CAS,有可能Thread1取到了myValue1,Thread2也取到了myValue1,然后相加,Entry中的value值为2。这与预期不相符,我们预期的是Entry的值经过两次相加后等于3。
  CAS会先把Entry现在的value跟线程当初读出的值相比较,若相同,则赋值;若不相同,则赋值执行失败。一般会通过whilefor循环来重新执行,直到赋值成功。CAS无需线程进行上下文切换到内核态去执行,在用户态执行了CPU的原语指令cmpxchg,CAS相当于在用户态代码里边插入了一个cmpxchg指令,这样CPU一直在用户态执行,执行到cmpxchg指令就开始执行内核态内存空间的操作系统的代码。执行指令要比上下文切换的开销要小,所以CAS要比重量级互斥锁性能要高。(用户态和内核态没有切换)
  如果程序的关键部分比计数器的简单增量更复杂,则可能需要使用多个CAS操作的复杂状态机来编排争用。使用锁开发并发程序是困难的;而使用CAS操作和内存屏障开发无锁算法要更加复杂多倍,而且难于测试和证明正确性。内存屏障和缓存问题
  出于提升性能的原因,现代处理器执行指令、以及内存和执行单元之间数据的加载和存储都是不保证顺序的。不管实际的执行顺序如何,处理器只需保证与程序逻辑的顺序产生相同的结果即可。这在单线程的程序中不是一个问题。但是,当线程共享状态时,为了确保数据交换的成功与正确,在需要的时候、内存的改变能够以正确的顺序显式是非常重要的。处理器使用内存屏障来指示内存更新顺序很重要的代码部分。它们是在线程之间实现硬件排序和更改可见性的方法。
  内存屏障(英语:Memorybarrier),也称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,它使得CPU或编译器在对内存进行操作的时候,严格按照一定的顺序来执行,也就是说在内存屏障之前的指令和之后的指令不会由于系统优化等原因而导致乱序。
  大多数处理器提供了内存屏障指令:完全内存屏障(fullmemorybarrier)保障了早于屏障的内存读写操作的结果提交到内存之后,再执行晚于屏障的读写操作。内存读屏障(readmemorybarrier)仅确保了内存读操作;内存写屏障(writememorybarrier)仅保证了内存写操作。
  现代的CPU现在比当前一代的内存系统快得多。为了弥合这一鸿沟,CPU使用复杂的高速缓存系统,这些系统是有效的快速硬件哈希表,无需链接。这些缓存通过消息传递协议与其他处理器缓存系统保持一致。此外,处理器还具有存储缓冲区(storebufferloadbuffer,比L1缓存更靠近CPU,跟寄存器同一个级别,用来当作CPU与高速缓存之间的缓冲。毕竟高速缓存由于一致性的问题也会阻塞)来缓冲对这些缓存的写入,以及作为失效队列,以便缓存一致性协议能够在即将发生写入时快速确认失效消息,以提高效率。
  这对数据意味着,任何值的最新版本在被写入后的任何阶段都可以位于寄存器、存储缓冲区、L1L2L3缓存之一或主内存中。如果线程要共享此值,则需要以有序的方式使其可见,这是通过协调缓存一致性消息的交换来实现的。这些信息的及时产生可以通过内存屏障来控制。
  L1、L2、L3分别表示一级缓存、二级缓存、三级缓存,越靠近CPU的缓存,速度越快,容量也越小。所以L1缓存很小但很快,并且紧靠着在使用它的CPU内核;L2大一些,也慢一些,并且仍然只能被一个单独的CPU核使用;L3更大、更慢,并且被单个插槽上的所有CPU核共享;最后是主存,由全部插槽上的所有CPU核共享。
  当CPU执行运算的时候,它先去L1查找所需的数据、再去L2、然后是L3,如果最后这些缓存中都没有,所需的数据就要去主内存拿。走得越远,运算耗费的时间就越长。所以如果你在做一些很频繁的事,你要尽量确保数据在L1缓存中。
  另外,线程之间共享一份数据的时候,需要一个线程把数据写回内存,而另一个线程访问内存中相应的数据。
  如果你用一种能被预测的方式访问内存的话,CPU可以预测下个可能访问的值从内存先缓存到缓存中,来降低下次访问的延迟。但是如果是一些非顺序的、步长无法预测的结构,让CPU只能访问内存,性能上与访问缓存差很多。所以为了有效利用CPU高速缓存的特性,我们应当尽量使用顺序存储结构。队列的问题
  队列通常使用链表或数组作为元素的底层存储。如果允许内存中的队列是无界的,那么对于许多类的问题,它可以不受约束地增长,直到耗尽内存而达到灾难性的后果,当生产者超过消费者时就会发生这种情况。无界队列在可以在生产者可以保证不超过消费者的系统中使用,因为内存是一种宝贵的资源,但是如果这种假设不成立,而队列增长没有限制,那么总是有风险的。为了避免这种灾难性的结果,队列的大小通常要受到限制(有界)。要使队列保持有界,就需要对其底层选择数组结构或主动跟踪其大小。
  队列的实现往往要在head、tail和size变量上有写争用。在使用时,由于消费者和生产者之间的速度差异,队列通常总是接近于满或接近于空。它们很少在生产和消费速率均衡的中间地带运作。这种总是满的或总是空的倾向会导致高级别的争用、和或昂贵的缓存一致性。问题在于,即使head和tail使用不同的并发对象(如锁或CAS变量)来进行读写锁分离,它们通常也占用相同的cacheline。
  管理生产者申请队列的head,消费者申请队列的tail,以及中间节点的存储,这些问题使得并发实现的设计非常复杂,除了在队列上使用一个粗粒度的锁之外,还难以管理。对于put和take操作,使用整个队列上的粗粒度锁实现起来很简单,但对吞吐量来说是一个很大的瓶颈。如果并发关注点在队列的语义中被分离开来,那么对于除单个生产者单个消费者之外的任何场景,实现都变得非常复杂。
  而使用相同的cacheline会产生伪共享问题。比如ArrayBlockingQueue有三个成员变量:takeIndex:需要被取走的元素下标;putIndex:可被元素插入的位置的下标;count:队列中元素的数量;
  这三个变量很容易放到一个缓存行中,但是之间修改没有太多的关联。所以每次修改,都会使之前缓存的数据失效,从而不能完全达到共享的效果。
  如上图所示,当生产者线程put一个元素到ArrayBlockingQueue时,putIndex会修改,从而导致消费者线程的缓存中的缓存行无效,需要从主存中重新读取。一般的解决方案是,增大数组元素的间隔使得由不同线程存取的元素位于不同的缓存行上,以空间换时间。Disruptor解决思路
  启动时,将预先分配环形缓冲区的所有内存。环形缓冲区可以存储指向entry的指针数组,也可以存储表示entry的结构数组。这些entry中的每一个通常不是传递的数据本身,类似对象池机制,而是它的容器。这种entry的预分配消除了支持垃圾回收的语言中的问题,因为entry将被重用,并在整个Disruptor实例存活期间都有效。这些entry的内存是同时分配的。
  一般的数据结构是像下面这样的:
  我们可以使用一个环状的数组结构改进成下面这样:
  数组的连续多个元素会一并加载到CPUCache里面来,所以访问遍历的速度会更快。而链表里面各个节点的数据,多半不会出现在相邻的内存空间,自然也就享受不到整个CacheLine加载后数据连续从高速缓存里面被访问到的优势。遍历访问时CPU层面的分支预测会很准确。这可以使得我们更有效地利用了CPU里面的多级流水线,我们的程序就会跑得更快。
  在像Java这样的托管运行时环境中开发低延迟系统时,垃圾收集机制可能会带来问题。分配的内存越多,给垃圾收集器带来的负担就越大。当对象的寿命很短或实际上是常驻的时候,垃圾收集器工作得最好。在环形缓冲区中预先分配entry意味着它对于垃圾收集器来说是常驻内存的,垃圾回收的负担就很轻。同时,数组结构对处理器的缓存机制更加友好。数组长度2n,通过位运算,加快定位的速度。下标采取递增的形式。不用担心index溢出的问题。index是long类型,即使100万QPS的处理速度,也需要30万年才能用完。
  一般的CacheLine大小在64字节左右,然后Disruptor在非常重要的字段前后加了很多额外的无用字段。可以让这一个字段占满一整个缓存行,这样就可以避免未共享导致的误杀。
  每个生产者或者消费者线程,会先申请可以操作的元素在数组中的位置,申请到之后,直接在该位置写入或者读取数据。
  下面用非环形的结构模拟无锁读写。一个生产者的流程申请写入m个元素;若是有m个元素可以入,则返回最大的序列号。这儿主要判断是否会覆盖未读的元素;若是返回的正确,则生产者开始写入元素。
  多个生产者流程
  多个生产者的情况下,会遇到如何防止多个线程重复写同一个元素的问题。Disruptor的解决方法是,每个线程获取不同的一段数组空间进行操作。这个通过CAS很容易达到。只需要在分配元素的时候,通过CAS判断一下这段空间是否已经分配出去即可。
  但如何防止读取的时候,读到还未写的元素。Disruptor在多个生产者的情况下,引入了一个与RingBuffer大小相同的buffer,AvailableBuffer。当某个位置写入成功的时候,便把AvailbleBuffer相应的位置置位,标记为写入成功。读取的时候,会遍历AvailableBuffer,来判断元素是否已经就绪。读数据流程
  生产者多线程写入的情况会复杂很多:申请读取到序号n;若writercursorn,这时仍然无法确定连续可读的最大下标。从readercursor开始读取availableBuffer,一直查到第一个不可用的元素,然后返回最大连续可读元素的位置;消费者读取元素。
  如下图所示,读线程读到下标为2的元素,三个线程Writer1Writer2Writer3正在向RingBuffer相应位置写数据,写线程被分配到的最大元素下标是11。
  读线程申请读取到下标从3到11的元素,判断writercursor11。然后开始读取availableBuffer,从3开始,往后读取,发现下标为7的元素没有生产成功,于是WaitFor(11)返回6。
  然后,消费者读取下标从3到6共计4个元素(多个生产者情况下,消费者消费过程示意图)。
  写数据流程
  多个生产者写入的时候:申请写入m个元素;若是有m个元素可以写入,则返回最大的序列号。每个生产者会被分配一段独享的空间;生产者写入元素,写入元素的同时设置availableBuffer里面相应的位置,以标记自己哪些位置是已经写入成功的。
  如下图所示,Writer1和Writer2两个线程写入数组,都申请可写的数组空间。Writer1被分配了下标3到下表5的空间,Writer2被分配了下标6到下标9的空间。
  Writer1写入下标3位置的元素,同时把availableBuffer相应位置置位,标记已经写入成功,往后移一位,开始写下标4位置的元素。Writer2同样的方式。最终都写入完成。
  总结
  整体上来看Disruptor在提高吞吐量、减少并发执行损耗上做出了很大贡献,通过贴合硬件机制的方式进行设计,消除写争用,最小化读争用,并确保代码与现代处理器使用的Cache特性良好配合。我们可以看下Log4j2的性能数据,Log4j2的Loggersallasync就是基于Disruptor的。
  总结来说Disruptor是性能极高的无锁队列,提供了一种很好的利用硬件特性实现尽可能从缓存读取来加速访问的无锁方案。

KPL夏季赛最佳阵容候选人WB四人入选,狼队和eaStar全员上榜昨日,KPL王者荣耀职业联赛官方微博发布了一条关于KPL夏季赛最佳阵容的博文,公布了2022年度KPL夏季赛最佳阵容候选人名单。值得一提的是,本次候选人名单中,打过夏季赛决赛的重庆R星官网更新致谢名单,告别GTA5?或准备正式公开GTA6?相信许多小伙伴都有玩过侠盗猎车手系列GTA5吧,但你们可能不知道的是这款游戏已经接近了9岁的高龄了。近日r星似乎要更新官网thankyou的致谢名单,这一举动是否在告别GTA5?打华为鸿蒙3。0公测版来了,第一批公测版名单出炉,看看有没有你?就在今天,也就是2022年9月6日,华为mate50系列手机正式发布,搭载的操作系统是华为鸿蒙3。0正式版,这也就标志着鸿蒙3。0系统已经开始成熟。伴随华为mate50的发布,华为幼儿园开学要带两盆植物,爷爷直接从地里挖来两颗苗网友实在人近日,萧山一网友在网上发帖称幼儿园不是开学需要两盆植物爷爷说那简单于是从地里挖来了两颗苗不少家长说这目测是班里乃至整个幼儿园最大的绿植了毕竟自家娃带去的都是小小的多肉仙人球之类的小吴镇宇14岁儿子确诊,曾因录爸爸留下永久性伤疤,网友可怜娱乐圈是个不缺新闻的地方,无论是什么咖位的艺人们,只要家里有点鸡毛蒜皮的事情,发到网上后就会被迅速炒起来,火的程度连当事人都觉得匪夷所思。吴镇宇和儿子吴费曼是靠着录制爸爸去哪儿,成工信部聚焦重点领域数字化需求,加快智能装备产业化9月6日,工信部举办了大力发展高端装备制造业发布会。工信部装备工业一司司长王卫明指出,下一步,工信部将按照党中央国务院的决策部署,加强部门沟通协作,制定发布专项政策文件,加快推动装尾盘前30分钟,主力资金最大卖出这100股名单若不喜欢看表格,输入任何数字进入底部图片区域。收盘前30分钟,主力资金大幅卖出最多的100股时间2022年9月6日142637序号代码名称最新价涨跌幅主力净流入主力净占比1hr30后室都市恐怖新游FootageSteam开启抢测近期在视频界爆红的后室都市恐怖题材系列再次推出新游,后室Footage日前在Steam开启抢测,暂不支持中文,那摇曳不定的镜头与贴身压迫的恐怖感再次袭来。后室FootageStea明日方舟新人配队分析先锋桃子尽快精二,一技能专三。风笛桃子基本上已经可以完成爆费工作。德狗拉到七级技能。近卫棘刺3专三银灰3专三山精二2专3煌精二2专3。棘刺,山,煌是三基石,抄作业经常会用。早拉起来2048数字游戏假如2048与三消合成组合成游戏是种怎样的体验头条群星8月榜以三消游戏的玩法玩2048是种怎样的体验?今天推荐由InspiredSquareFZE推出的以2048为背景益智类三消玩法为核心的趣味游戏2248NumberGameSD高达激斗同盟快速刷钱攻略SD高达激斗同盟游戏中玩家对于金币的要求还是很多的,在培养和升级的过程中会有大量的消耗,因此刷钱是非常必要的一个过程,可能有玩家还不知道怎么刷钱比较快,下面小编就给大家整理饿了具体
中国拥有一块海外领土?首先说答案,法理上是没有的。但是我们却拥有北极圈斯瓦尔巴群岛的使用权,要特别说明的是该岛屿主权归属于挪威,它位于巴伦支海和格陵兰海之间,总面积约6万平方公里。我们可以不用签证就可以河北大山中一小村竟然与浙江杭州的于谦有血脉渊源什么是历史?历史是留给后世人慨叹的。当我走进河北省井陉县城微水镇于家石头村里,看到这个深藏在巍巍太行怀抱中的小村庄不禁发出了这样的问答。因为,我想不到,这个小小村落竟然与青史留名要浅谈克里米亚半岛乌克兰与俄罗斯我两手空空,来到俄国,现在我终于给俄国带来了我的嫁妆,就是克里米亚和波兰。这句话是叶卡捷琳娜女皇叶卡捷琳娜女皇对自己功绩的评价。从公元17世纪开始,为了争夺克里米亚半岛,俄国与奥斯三季度成绩单揭晓,中国移动正在拉开差距!但仍存一隐忧近日,三大运营商先后发布了三季度财报,整体上,通信运营行业仍然延续自2020年最后一季开始的V型反转趋势,从宏观层面,三大运营商营业收入以三倍GDP增速的速度在增长,利润增速更为可美国会不会关闭GPS信号?爱关不关,全球已经137个国家用中国北斗GPS属于是全球定位系统的其中之一,对于使用者以及使用国家,都有着极具意义的重要性。且当开发国家(美国)对某个地区关闭掉该信号之后,将产生不同程度的损害,且尤其是在安全问题方面的损中国石油化工股份终止美国存托股计划于2022年12月5日生效中国石油化工股份11月2日在港交所公告,公司存托股的退市已于2022年9月8日(美国东部时间)生效。根据董事会的适当授权,公司已于2022年11月2日向公司的存托股存托银行花旗银行中国商用车及配套产业博览会在物流汽运之都江西高安举行中国物流汽运之都江西高安市又迎来了一年一度的行业盛会。11月1日,2022中国商用车及配套产业博览会在高安市开幕。图说2022中国商用车及配套产业博览会开幕式现场图澎湃新闻记者宋江10月中国物流业景气指数公布物流运行有所波动基本保持平稳态势央视网消息中国物流与采购联合会今天(11月2日)公布10月份中国物流业景气指数。受疫情和季节性因素影响,物流业景气指数有所回调,物流运行有所波动,但基本保持平稳态势,稳产保供保通保这个日本人用一生证明中国哲学的伟大,咱们凭什么文化不自信?提起中国哲学,我们一下子就能想到论语道德经易经这些,这是我们中国哲学和传统文化的根,但是很多人却已经看不懂了,甚至不想看了。再近一点,500多年前,王阳明创立了阳明心学,凭借一己之中国气象局党组召开会议传达学习习近平总书记近期重要讲话精神推动部门深入学习宣传贯彻党的二十大精神11月2日,中国气象局党组书记局长庄国泰主持召开党组会,传达学习10月25日二十届中共中央政治局会议和习近平总书记在二十届中共中央政治局第一次集体学习在瞻仰延安革命纪念地以及在陕西外媒德国告诉塞尔维亚,必须在欧盟和俄罗斯间做出选择德国周二告诉贝尔格莱德,塞尔维亚必须在加入欧盟和与俄罗斯建立伙伴关系之间做出选择,路透社11月1日这样报道说。路透社德国告诉塞尔维亚必须在欧盟和俄罗斯之间做出选择报道称,一位德国政
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网