沧州三亚菏泽经济预测自然
投稿投诉
自然科学
知识物理
化学生物
地理解释
预测理解
本质社会
人类现象
行为研究
经济政治
心理结构
关系指导
人文遗产
菏泽德阳
山西湖州
宝鸡上海
茂名内江
三亚信阳
长春北海
西安安徽
黄石烟台
沧州湛江
肇庆鹤壁
六安韶关
成都钦州

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

9月5日 浅时光投稿
  介绍
  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是性能极高的无锁队列,提供了一种很好的利用硬件特性实现尽可能从缓存读取来加速访问的无锁方案。
投诉 评论

船大难掉头?百年通用上演逆转好戏,凭实力领跑电气化赛道自2014年,广州车展为电动汽车单独开辟展厅开始,新能源汽车在中国市场就迎来了黄金发展期,也让中国消费者近距离感受到了新能源智能汽车的魅力和优势。而每每提及新能源汽车,很多人就……同样是骑马,有人成鲜衣怒马少年郎,有人被骂退圈,差距一目了然虽说科技在进步,影视剧也在不断成长,但依然觉得以前的才更加原汁原味。尽管滤镜、特效还没那么先进,但不管是拍打戏还是骑马的戏份,演员大多是亲自上场,给人的感觉很是真实。……盘点40款各大乐高乐园限定套装乐高乐园(LEGOLAND),是乐高集团于1968年开启的乐高积木主题游乐园。首家乐高乐园于1968年开设在丹麦比隆,后续在英国、德国、美国加州等地陆续建设。世界上第一家……OPPO系海外创业者,想独立没那么容易本文来自微信公众号:银杏科技(ID:yinxingcj),作者:零零柒,原文标题:《OPPO系与它的海外门生》,题图来自:视觉中国尽管互联网当下不再是主流叙事,但这高速发……关于Disruptor高性能队列原理的浅析介绍Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与IO操作处于同样的数量级)。基于Disru……2022渐渐躺平90后该何去何从?不知道从什么时候开始,大约在2012年开始,突然觉得时间异常的短暂,一转眼,90后们一个异常宝贵的青春10年突然过去,回首往日,你有对什么事情后悔过吗?12年进入大学生活……提醒你,千万别小瞧帽子!今年流行这4款,戴上之后洋气减龄如果我们拥有可以变美的机会,相信很多人都会拼尽全力的去把握,尤其是对于很多爱美的女人来说,面对着能够达到减龄效果,并且洋气感十足的帽子,相信很多人都会入手好几顶!大家可千……万达王健林守得云开见月明恒大许家印,倒在黎明前一夜一家荒唐之言,只为博君一笑;切莫走心较真,阅后点赞就好。10月21日,《中国证券报》刊发文章《证监会:允许满足特定条件涉房地产企业在A股市场融资》。文章称,中国证券报记者……早安日历每日一签09152022年09月15日,农历八月二十,星期四,白露第九天。坚定信心,同舟共济;科学防治,精准施策;疫情防控人,人都有责任;坚决打赢疫情防控阻击战。每一份……孩子想学画画应该怎么选择少儿美术培训机构?VIP陪画怎么样?许多人问,为什么要教儿童绘画呢?父母们也越来越重视这一点,自从美育被重新提上日程,这一问题再次引起了社会各界的关注。那小孩想学画画该如何选择少儿美术培训机构?幼儿要画画,……结节囊肿根源在于肝郁!一碗攻坚汤,疏肝理气,消瘿散结百病起于心,情志乱则百病丛生。中医认为情志失调,肝气郁结,会导致全身气滞、痰凝、血瘀,诱发一系列疾病。一般情况下,人体气机顺畅,五脏六腑的气化功能是正常的,一旦七情内伤,……盘点屏幕采用2K分辨率的小米手机,共14款【1】小米Note顶配版搭载高通骁龙810处理器,采用台积电20nm的制程工艺,采用Adreno430旗舰级图形处理器,在运行3D游戏、浏览高分辨率图像时,都能体验到极速……
哈基姆。奥拉朱旺,用美学定义篮球199395两年,当芝加哥不再飞扬迈克尔。乔丹的球衣,时年31岁的哈基姆。奥拉朱旺终于迎来生涯巅峰。那一年,他前无古人后无来者的包揽了常规赛MVP、总决赛MVP、年度防守球员。……为什么很多老年人活过七十岁就去世了?告诉你3个原因,3个忠告导语人们常说七十古来稀,到了七十岁这个年龄,若想健康长寿,并非容易之事。人到老年,器官老龄化情况变得更严重,身体每况愈下,如果身体不是很健康,老伴又先离开人世,那么当你过……5分惜败!亚洲冠军或无缘8强,中国女篮淘汰赛对手浮现再战苦主北京时间9月23日,女篮世界杯进入到第二个比赛日的争夺,B组第二轮的一场强强对话中,塞尔维亚女篮对阵日本女篮。这场比赛的结果会直接影响B组的出线形势,甚至会间接影响中国女篮在淘……遥望网络互联网营销师在央视频道意外走红,年直播400多场当你还以为直播带货是个不入流的职业时,央视也看不下去出来官方正名了!3月3日,杭州的遥望网络电商直播产业园再次登上了央视2台的《经济半小时》节目,狠狠刷了一波存在感。记者……勇士对阵国王5大策略库里上场时间,限制双枪,保护篮板金州勇士首战错失了两记绝杀三分,在防守策略上过度盯住了萨博尼斯,从而忽略了蒙克的得分爆炸力。金州勇士已经掌握了国王队的两种打法,要快速在两种防守方式上转换,才能战胜对手。以国王……设计先驱GaetanoPesce打造的BottegaVene于时装品牌BottegaVeneta2023春夏秀场上大放异彩的82岁设计先驱GaetanoPesce所打造的彩色座椅近日无预警在品牌官网开售。这些座椅名为ComeSta……秦海璐36岁时冒险生下巨婴症儿子,多年过去,孩子成了这样婚姻路上分分合合对现在的人们来说都非常的正常,特别是女性地位的提高,现在的女性们都能非常独立自主的生活,不再是男人的附属品。秦海璐和王新军的爱情也算是一波三折,两人各自曾有过婚……浪姐328位成员吸金能力差很大,8人赚得盆满钵满,5人挂零蛋《乘风破浪的姐姐》已经办到第三季,为什么有近百位女明星愿意参加呢?喜欢舞台、有女团梦是一回事,名利双收的诱惑亦是一大原因,毕竟不少人凭借这档综艺翻红,名气和身价今非昔比。……除了故宫,北京市区赏雪还能去哪儿?这个宝藏公园,你要的都有北海公园,是我心目中最宝藏的北京公园之一。不仅位置方便,而且一年四季景色不断。如果想抓住短暂的时间赏雪,去北海吧,还可以顺便去景山或者故宫。北海赏雪,首推几个园中园……这份攻略,坐火车返程一定要看不知不觉,春节假期已接近尾声。这份返程攻略送给即将乘车返程的你。出发前,检查下行李有些在生活中常见的物品,不能携带上火车。如酒精、儿童玩具枪、超过120毫升的压力罐……ChatGPT甘肃省哪三个城市最有发展潜力?根据甘肃省的地理位置、交通、经济、教育资源、科技创新、旅游和人口等因素,以下是我认为最具潜力的三个城市:兰州市兰州市位于甘肃省中部,是甘肃省的省会城市,也是甘肃省的政治、……400万人逃离保险行业,还有人敢抄底保险股吗?保险行业最近几年的日子不太好过。中国平安的股价在2020年创出88元(前复权价)的新高后就一路下滑,有个段子是这么说的:如果你不珍惜80元的平安,就只能珍惜70元的平安了……

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找