Disruptor 高性能堆内队列 系列二
本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)
本文作者: Nicksxs
创建时间: 2022-02-27
本文链接: Disruptor 高性能堆内队列 系列二
这里开始慢慢深入的讲一下 disruptor,首先是 lock free
, 相比于前面介绍的两个阻塞队列,
disruptor 本身是不直接使用锁的,因为本身的设计是单个线程去生产,通过 cas 来维护头指针,
不直接维护尾指针,这样就减少了锁的使用,提升了性能;第二个是这次介绍的重点,
减少 false sharing
的情况,也就是常说的 伪共享 问题,那么什么叫 伪共享 呢,
这里要扯到一些 cpu 缓存的知识,
譬如我在用的这个笔记本
这里就可能看到 L2 Cache 就是针对每个核的
这里可以看到现代 CPU 的结构里,分为三级缓存,越靠近 cpu 的速度越快,存储容量越小,
而 L1 跟 L2 是 CPU 核专属的每个核都有自己的 L1 和 L2 的,其中 L1 还分为数据和指令,
像我上面的图中显示的 L1 Cache 只有 64KB 大小,其中数据 32KB,指令 32KB,
而 L2 则有 256KB,L3 有 4MB,其中的 Line Size 是我们这里比较重要的一个值,
CPU 其实会就近地从 Cache 中读取数据,碰到 Cache Miss
就再往下一级 Cache 读取,
每次读取是按照缓存行 Cache Line
读取,并且也遵循了“就近原则”,也就是相近的数据有可能也会马上被读取,所以以行的形式读取,然而这也造成了 false sharing
,
因为类似于 ArrayBlockingQueue
,需要有 takeIndex
, putIndex
, count
, 因为在同一个类中,
很有可能存在于同一个 Cache Line
中,但是这几个值会被不同的线程修改,
导致从 Cache 取出来以后立马就会被失效,所谓的就近原则也就没用了,因为需要反复地标记 dirty 脏位,然后把 Cache 刷掉,就造成了false sharing
这种情况而在 disruptor
中则使用了填充的方式,让我的头指针能够不产生false sharing
通过代码可以看到,sequence 中其实真正有意义的是 value 字段,因为需要在多线程环境下可见也
使用了volatile
关键字,而 LhsPadding
和 RhsPadding
分别在 value 前后填充了各
7 个 long
型的变量,long
型的变量在 Java 中是占用 8 bytes,这样就相当于不管怎么样,
value 都会单独使用一个缓存行,使得其不会产生 false sharing
的问题。
版权声明: 本文为 InfoQ 作者【Nick】的原创文章。
原文链接:【http://xie.infoq.cn/article/55ca7fedd578eed582d9153d7】。文章转载请联系作者。
评论