欢迎来到天天文库
浏览记录
ID:43949933
大小:167.00 KB
页数:5页
时间:2019-10-17
《剖析Disruptor为什么会这么快(3):揭秘内存屏障-编程开发技术》由会员上传分享,免费在线阅读,更多相关内容在工程资料-天天文库。
1、剖析Disruptor为什么会这么快(3):揭秘内存屏障-编程开发技术剖析Disruptor为什么会这么快(3):揭秘内存屏障译文出处:杜建雄原文出处:Trisha最近我博客文章更新冇点慢,因为我在忙着写一篇介绍内存屏障(MemoryBarries)以及如何将其应用于Disruptor的文章。问题是,无论我翻阅了多少资料,向耐心的Martin和Mike请教了多少遍,以试图理清一些知识点,可我总是不能直观地抓到重点。大概是因为我不具备深厚的背景知识來帮助我透彻理解。所以,与其像个傻瓜一样试图去解释一些自己都没完全弄懂的东西,还不如在抽象和大量简化的层次上,把我在该领
2、域所掌握的知识分享给大家。Martin已经写了一篇文章《goingintomemorybarriers》介绍内存屏障的一些具体细节,所以我就略过不说了。免责声明:文章中如有错误全由本人负责,与Disruptor的实现和LMAX里真止懂这些知识的大牛们无关。主题是什么?我写这个系列的博客主耍目的是解析Disruptor是如何工作的,并深入了解下为什么这样工作。理论上,我应该从可能准备使用disruptor的开发人员的角度来写,以便在代码和技术论文[Disruptor-1.0.pdf]2,间搭建一座桥梁。这篇文章提及到了内存屏障,我想弄清楚它们到底是什么,以及它们是如
3、何应用于实践中的。什么是内存屏障?它是一个CPU指令。没错,又一次,我们在讨论CPU级别的东西,以便获得我们想要的性能(Martin著名的MechanicalSympathy理论)。基本上,它是这样一条指令:a)确保一些特定操作执行的顺序;b)彩响一些数据的可见性(可能是某些指令执行后的结果)。编译器和CPU可以在保证输岀结果一样的情况下对指令重排序,使性能得到优化。插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。正如去拉斯维加斯旅途屮齐个站点的先后顺序在你心中都一清二楚。bcKW«ICoe4、更新一次不同CPU的缓存。例如,-•个写屏障会把这个屏障前写入的数据刷新到缓存,这样任何试图读取该数据的线程将得到最新值,而不用考虑到底是被哪个CPU核心或者哪颗CPU执行的。和Java有什么关系?现在我知道你在想什么——这不是汇编程序。它是Javao这里有个神奇咒语叫vobt订c(我觉得这个词在Java规范小从未被解释清楚)。如果你的字段是volatile,Java内存模型将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令。5、C6、这意味着如果你对一个volatile字段进行写操作,你必须知道:1、一旦你完成写入,任何访问这个字段的线程将会得7、到最新的值。2、在你写入前,会保证所有2前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。举个例子呗!很高兴你这样说了。又是时候讣我来冊i几个甜甜圈了。RingBuffer的指针(cursor)(译注:指向队尾元素)属于一个神奇的volatile变量,同时也是我们能够不用锁操作就能实现Disruptor的原因之一。生产者将会取得下一个Entry(或者是一批),并可对它(们)作任意改动,把它(们)更新为任何想要的值。如你所知,在所有改动都完成后,生产者对ringbuffer调用commit方法来更新序列号(译注:把cur8、sor更新为该Entry的序列号)。对volatile字段(cursor)的写操作创建了一个内存屏障,这个屏障将刷新所有缓存里的值(或者至少相应地使得缓存失效)。这时候,消费者们能获得最新的序列号码(8),并且因为内存屏障保证了它之前执行的指令的顺序,消费者们可以确信生产者对7号Entry所作的改动已经可用。…那么消费者那边会发生什么?消费者中的序列号是volatile类型的,会被若干个外部对象读取——其他的下游消费者可能在跟踪这个消费者。ProducerBarrier/RingBuffer(取决于你看的是旧的还是新的代码)跟踪它以确保环没冇出现重叠(wrap)的9、情况(译注:为了防止卜•游的消费者和上游的消费者对同一个Entry竞争消费,导致在环形队列屮互相覆盖数据,下游消费者耍对上游消费者的消费情况进行跟踪)。所以,如果你的下游消费者(C2)看见前一个消费者(Cl)在消费号码为12的Entry,当C2的读取也到了12,它在更新序列号前将可以获得C1对该Entry的所作的更新。基本來说就是,C1更新序列号前对ringbuffer的所有操作(如上图黑色所示),必须先发生,待C2拿到C1更新过的序列号之后,C2才可以为所欲为(如上图蓝色所示)。对性能的影响内存屏障作为另一个CPU级的指令,没有锁那样大的开销。内核并没有在多个线10、程间干涉和
4、更新一次不同CPU的缓存。例如,-•个写屏障会把这个屏障前写入的数据刷新到缓存,这样任何试图读取该数据的线程将得到最新值,而不用考虑到底是被哪个CPU核心或者哪颗CPU执行的。和Java有什么关系?现在我知道你在想什么——这不是汇编程序。它是Javao这里有个神奇咒语叫vobt订c(我觉得这个词在Java规范小从未被解释清楚)。如果你的字段是volatile,Java内存模型将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令。
5、C6、这意味着如果你对一个volatile字段进行写操作,你必须知道:1、一旦你完成写入,任何访问这个字段的线程将会得7、到最新的值。2、在你写入前,会保证所有2前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。举个例子呗!很高兴你这样说了。又是时候讣我来冊i几个甜甜圈了。RingBuffer的指针(cursor)(译注:指向队尾元素)属于一个神奇的volatile变量,同时也是我们能够不用锁操作就能实现Disruptor的原因之一。生产者将会取得下一个Entry(或者是一批),并可对它(们)作任意改动,把它(们)更新为任何想要的值。如你所知,在所有改动都完成后,生产者对ringbuffer调用commit方法来更新序列号(译注:把cur8、sor更新为该Entry的序列号)。对volatile字段(cursor)的写操作创建了一个内存屏障,这个屏障将刷新所有缓存里的值(或者至少相应地使得缓存失效)。这时候,消费者们能获得最新的序列号码(8),并且因为内存屏障保证了它之前执行的指令的顺序,消费者们可以确信生产者对7号Entry所作的改动已经可用。…那么消费者那边会发生什么?消费者中的序列号是volatile类型的,会被若干个外部对象读取——其他的下游消费者可能在跟踪这个消费者。ProducerBarrier/RingBuffer(取决于你看的是旧的还是新的代码)跟踪它以确保环没冇出现重叠(wrap)的9、情况(译注:为了防止卜•游的消费者和上游的消费者对同一个Entry竞争消费,导致在环形队列屮互相覆盖数据,下游消费者耍对上游消费者的消费情况进行跟踪)。所以,如果你的下游消费者(C2)看见前一个消费者(Cl)在消费号码为12的Entry,当C2的读取也到了12,它在更新序列号前将可以获得C1对该Entry的所作的更新。基本來说就是,C1更新序列号前对ringbuffer的所有操作(如上图黑色所示),必须先发生,待C2拿到C1更新过的序列号之后,C2才可以为所欲为(如上图蓝色所示)。对性能的影响内存屏障作为另一个CPU级的指令,没有锁那样大的开销。内核并没有在多个线10、程间干涉和
6、这意味着如果你对一个volatile字段进行写操作,你必须知道:1、一旦你完成写入,任何访问这个字段的线程将会得
7、到最新的值。2、在你写入前,会保证所有2前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。举个例子呗!很高兴你这样说了。又是时候讣我来冊i几个甜甜圈了。RingBuffer的指针(cursor)(译注:指向队尾元素)属于一个神奇的volatile变量,同时也是我们能够不用锁操作就能实现Disruptor的原因之一。生产者将会取得下一个Entry(或者是一批),并可对它(们)作任意改动,把它(们)更新为任何想要的值。如你所知,在所有改动都完成后,生产者对ringbuffer调用commit方法来更新序列号(译注:把cur
8、sor更新为该Entry的序列号)。对volatile字段(cursor)的写操作创建了一个内存屏障,这个屏障将刷新所有缓存里的值(或者至少相应地使得缓存失效)。这时候,消费者们能获得最新的序列号码(8),并且因为内存屏障保证了它之前执行的指令的顺序,消费者们可以确信生产者对7号Entry所作的改动已经可用。…那么消费者那边会发生什么?消费者中的序列号是volatile类型的,会被若干个外部对象读取——其他的下游消费者可能在跟踪这个消费者。ProducerBarrier/RingBuffer(取决于你看的是旧的还是新的代码)跟踪它以确保环没冇出现重叠(wrap)的
9、情况(译注:为了防止卜•游的消费者和上游的消费者对同一个Entry竞争消费,导致在环形队列屮互相覆盖数据,下游消费者耍对上游消费者的消费情况进行跟踪)。所以,如果你的下游消费者(C2)看见前一个消费者(Cl)在消费号码为12的Entry,当C2的读取也到了12,它在更新序列号前将可以获得C1对该Entry的所作的更新。基本來说就是,C1更新序列号前对ringbuffer的所有操作(如上图黑色所示),必须先发生,待C2拿到C1更新过的序列号之后,C2才可以为所欲为(如上图蓝色所示)。对性能的影响内存屏障作为另一个CPU级的指令,没有锁那样大的开销。内核并没有在多个线
10、程间干涉和
此文档下载收益归作者所有