Disruptor-03 Ringbuffer的高性能设计
Ringbuffer数据结构
- 一维数组 构成 逻辑环
初步解读:
- 数组是连续的内存空间,连续内存空间的读写效率较高
- 仅申请一次空间,逻辑上构成环形结构,复用内存空间.大大减少了内存空间的申请和释放动作.
- FIFO,读取数据时,可以实现非常好的批处理.
生产者线程 和 消费者线程之间通信的桥梁(共享变量)
作为临界区资源,必然会有共享变量,为了保证线程安全所需要解决的问题:
- 变量修改的可见性
- 变量修改的互斥性
通常的操作是:
- 原子性修改.(循环+CAS) ,性能高
- 修改动作串行化, 性能低
知道怎么操作后,还需要进一步思考以下几个问题:
- 串行化,可见性的粒度怎样
- 原子性的修改,改的是什么?
- 可见性是什么可见?
线程安全问题的关键也在于"变",对于这个队列来说变得部分需要我们必须关注的是:
- 生产者:消息待放置的位置
- 消费者:可消费的消息的位置.
特别要留意: ringbuffer数组大小是固定的,没有变化.
ringbuffer中的循环+cas操作
对上边变的部分,从源码中看可以知道,是通过循环+cas操作来保证数据的安全性的
变更可见性以及隔离控制(缓存行填充 难度中)
生产者往某个元素中放置了数据后,消费者要能知道(可见),
消费者消费完了某个位置的元素后,生产者要能知道(可见),
以面向对象的思想来考虑,我们将这些信息作为属性放置到RingBuffer这个对象中,生产者,消费者在不同线程中,通过RingBuffer这个对象的相关属性获取这些位置信息
那么这个RingBuffer对象的内存,在线程间共享,其属性变量(内存中的数据)的修改要保证线程间可见.
这个线程间到底是什么样的线程之间??? 运行在不同cpu核的线程之间?不仅仅是这样,这里的关键是要梳理一下计算机组成原理以及操作系统的一些知识.
思考一个问题:Netty里设置线程池中线程数目的时候,通常设置为多少,有么有一个参考基数? 答案里有一个CPU核数.那么CPU核与线程之间什么关系呢?
CPU的线程数概念仅仅只针对Intel的CPU才有用,因为它是通过Intel超线程技术来实现的,最早应用在Pentium4上。如果没有超线程技术,一个CPU核心对应一个线程。所以,对于AMD的CPU来说,只有核心数的概念,没有线程数的概念。
在没有超线程这种情况下,CPU的核数也就是并行运行的线程数.
CPU怎么操作内存中的数据?
为了弥补CPU和内存两者之间的速度差异,充分使用把CPU的高性能,而不是让它在那儿空转等待,现代CPU架构中引入了高速缓存,分为L1 cache,L2 cache和L3 cache
image.png内存中的指令、数据,会被加载到高速缓存,在95%的情况下,CPU都只需要访问L1-L3 Cache,而不是直接 由CPU访问内存去拿。
image.pngMESI
缓存一致性问题,操作系统通过MESI这么个协议,达到一个最终一致性,可是对应用程序来说在时效性上没有保证.
内存屏障
为了在时效性上有保证,我们在程序中明确的要求时效性,通过给屏障指令.
感谢你们
001-CPU多级缓存架构
http://mechanitis.blogspot.com/2011/06/dissecting-disruptor-whats-so-special.html