Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RingBuffer内slots数组的补齐问题 #4

Closed
prontera opened this issue Aug 3, 2017 · 3 comments
Closed

RingBuffer内slots数组的补齐问题 #4

prontera opened this issue Aug 3, 2017 · 3 comments

Comments

@prontera
Copy link

prontera commented Aug 3, 2017

感谢百度开源了本项目,鄙人在研读源码的时候有点疑惑,希望能得到各位前辈的指点。

RingBuffer内有两个环形数组,一个名为slots作用为存储UID,另一个名为flags用于存储标志位。

具体引用代码如下

https://github.com/baidu/uid-generator/blob/master/src/main/java/com/baidu/fsg/uid/buffer/RingBuffer.java#L51-L52

slots中的元素唯一被填充的填充的地方是com.baidu.fsg.uid.buffer.RingBuffer#put,并通过线程池间接调用了该方法,多线程间对slots进行写操作在多核处理器下应该也会使得该数组发生伪共享问题,请问为什么不像flags一样地去使用PaddedAtomicLong进行补齐,或者是我对伪共享有错误的理解,望不吝赐教!

@baozhi
Copy link
Member

baozhi commented Aug 5, 2017

感谢对uid-generator项目的关注,很高兴一起交流探讨

请问为什么不像flags一样地去使用PaddedAtomicLong进行补齐

CPU Cache line是把双刃剑

  • 好处:可充分利用CPU缓存提升性能。RingBuffer.slots环填充的为UID,会频繁的被读取。所以为了提升获取UID的性能,未采用内存补齐,这样cache line中可放置更多的UID。

  • 隐患:伪共享问题。对于RingBuffer.take()操作,会频繁更新RingBuffer.cursorRingBuffer.flags,对写操作采用内存补齐的方式,可避免伪共享的隐患,又不影响读取性能。

RingBuffer#put,并通过线程池间接调用了该方法,多线程间对slots进行写操作

这里采用多线程读RingBuffer.take(),需要保证读的吞吐量;
串行写RingBuffer.put(),同一时刻只要有一个线程写即可,同时写操作采用synchronized串行化

另外,对于RingBuffer.put()有两种触发机制:

  1. 实时写,环上可用UID达到一定阈值(默认50%)时,实时填满Ringbuffer(默认开启)
  2. 定时写,基于定时周期填满(可选开启)

填充动作BufferPaddingExecutor.paddingBuffer(),均通过线程池异步执行,由BufferPaddingExecutor.running保证同一时刻只有一个填充任务被执行

@prontera
Copy link
Author

prontera commented Aug 5, 2017

十分感谢贡献者在百忙之中能抽出时间进行解答。

多线程间对slots进行写操作在多核处理器下应该也会使得该数组发生伪共享问题

这里的本意其实是

即使是不同线程间对slots进行串行写操作在多核处理器下应该也会使得该数组发生伪共享问题,因为Java线程在目前来说并不能绑定CPU,所以在修改相同的Cache Line的时候,是有十分可能产生RFO信号的

由于本人的纰漏让作者您产生了歧义表示十分的抱歉。但是解决了鄙人对该变量的类型设定的问题 -- 提高获取UID的性能。

再次感谢贡献者对本issue的耐心回答。

@baozhi baozhi closed this as completed Aug 6, 2017
@tanchao128
Copy link

tanchao128 commented Sep 17, 2020

slot只在put方法中被更新,而且put方法是单线程异步访问,后台线程对性能要求不高,没必要使用PaddedAtomicLong。
slot在take方法只是被读取,不使用PaddedAtomicLong,可以加载更多数据到cacheline,提高访问性能。
我是这么理解的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants