美文网首页
JCTools中的queue

JCTools中的queue

作者: rock_fish | 来源:发表于2020-01-03 17:29 被阅读0次

    所属文集:一起掌握并发

    Netty后来版本中使用了JCTools中的queue;

    JDK自带的queue和JCTools的queue对比效果

    100万数据
    括号中的213是秒数。比如4901/s (213) 用时213秒,平均每秒钟处理4901条。
    capacity 其值的大小对结果的影响是蛮大的

    Producer Consumer capacity LinkedBlockingQueue ArrayBlockingQueue MpscLinkedAtomicQueue MpscChunkedArrayQueue MpscArrayQueue
    128 1 128 4901/s (213) 38518/s (27) 17702070/s (0) 153652/s (6) 161788/s (6)
    128 1 256 10210/s (102) 64686/s (16) 21769821/s (0) 154687/s (6) 181169/s (5)
    128 1 512 26195/s (40) 99332/s (10) 20426455/s (0) 184272/s (5) 193574/s (5)
    128 1 1024 160711/s (6) 146096/s (7) 22314536/s (0) 108893/s (9) 213303/s (4)
    256 1 128 2518/s (416) 18079/s (57) 4699428/s (0) 55165/s (19) 91258/s (11)
    256 1 256 5418/s (193) 27639/s (37) 20966414/s (0) 56481/s (18) 74522/s (14)
    256 1 512 17290/s (60) 41326/s (25) 21005054/s (0) 68642/s (15) 111464/s (9)
    256 1 1024 27646/s (37) 81722/s (12) 20153991/s (0) 52241/s (20) 84719/s (12)

    MPSC的高性能操作总结。
    • Single Writer Principle(单写原则)
      MP是多写,没有这个原则。

    • lazy set

    1. lazySet是使用Unsafe.putOrderedObject方法,会前置一个store-store barrier(在当前的硬件体系下或者是no-op或者非常轻),而不是store-load barrier, store-load barrier较慢,总是用在volatile的写操作上。在操作序列Store1; StoreStore;Store2中,Store1的数据会在Store2和后续写操作之前对其它处理器可见。换句话说,就是保证了对其它数据可见的写的顺序。
      如果只有一个线程写我们就用不着store-load barrier,lazySet和volatile set在单写原则下面是等价的。 这种性能提升是有代价的,也就是写后结果并不会被其他线程看到,甚至是自己的线程,通常是几纳秒后被其他线程看到,lazySet的写在实践上来延迟是纳秒级,这个时间比较短,所以代价可以忍受。 类似Unsafe.putOrderedObject还有unsafe.putOrderedLong等方法,unsafe.putOrderedLong比使用 volatile long要快3倍左右,store-store的劣势是纳秒级的延迟。

    总结:在MPSC的场景下,写的时候只需通过 StoreStore 保证写的顺序不乱,因为当前线程只负责写进去就行了,不需要读取(可见)这个最新值,
    其他线程在需要读取的时候,在读取的操作中 加入load barrier 来保证;

    • 向buffer中写数据 StoreStore,保证写入顺序
        /**
         * An ordered store(store + StoreStore barrier) of an element to a given offset
         */
        public static <E> void soElement(E[] buffer, long offset, E e)
        {
            UNSAFE.putOrderedObject(buffer, offset, e);
        }
    
    • 从buffer中读数据 LoadLoad,保证读取最新数据。
        /**
         * A volatile load (load + LoadLoad barrier) of an element from a given offset.
         */
        @SuppressWarnings("unchecked")
        public static <E> E lvElement(E[] buffer, long offset)
        {
            return (E) UNSAFE.getObjectVolatile(buffer, offset);
        }
    

    扩展知识点:unsafe中提供的读写操作:

    //A volatile load (load + LoadLoad barrier) of an element from a given offset.
    //从对象的指定偏移量处获取变量的引用,使用volatile的加载语义
    public native Object getObjectVolatile(Object o, long offset);
    
    //存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义
    public native void putObjectVolatile(Object o, long offset, Object x);
    
    //有序、延迟版本的putObjectVolatile方法,不保证值的改变被其他线程立即看到。只有在field被volatile修饰符修饰时有效
    //An ordered store(store + StoreStore barrier) of an element to a given offset
    public native void putOrderedObject(Object o, long offset, Object x);
    
    -----------------
    //A plain store (no ordering/fences) of an element to a given offset
    //获得给定对象的指定地址偏移量的值,与此类似操作还有:getInt,getDouble,getLong,getChar等
    public native Object getObject(Object o, long offset);
    //给定对象的指定地址偏移量设值,与此类似操作还有:putInt,putDouble,putLong,putChar等
    public native void putObject(Object o, long offset, Object x);
    
    • 大量的位运算
      在前面中也看到了,通过&运算得到数组的下标,<< 计算数组的偏移地址,再利用unsafe进行设置等操作。举个例子%运算耗时是&的两倍。
    public static long calcElementOffset(long index, long mask)
        {
            return REF_ARRAY_BASE + ((index & mask) << REF_ELEMENT_SHIFT);
        }
    //index & mask 等同于 % 取余 ;取4的余数,mask =(4-1) 
    //REF_ELEMENT_SHIFT = 2 ;
    //<< REF_ELEMENT_SHIFT : 左移2 等同于 * 4
    
    感谢你们:

    高性能SPSC无锁队列设计之路
    Netty中Queue的实现
    新版Netty中,使用JCTools中的容器
    Netty解读:Jctools高性能无锁队列源码分析
    AtomicLong.lazySet是如何工作的?
    Java中的指针:Unsafe类

    思考Java中的指针:Unsafe类中的这段话:
    在Java中使用本地内存有它的意义。从延迟的角度来说,直接访问本地内存不会比访问Java堆快。这个结论其实是有道理的,因为跨越JVM屏障肯定是有开销的。这样的结论对使用本地还是堆的ByteBuffer同样适用。使用本地ByteBuffer的速度提升不在于访问这些内存,而是它可以直接与操作系统提供的本地IO进行操作。

    Java并发编程系列:漫谈伪共享
    Java并发编程系列:CAS 详解
    JVM系列:二、虚拟机中的对象布局

    相关文章

      网友评论

          本文标题:JCTools中的queue

          本文链接:https://www.haomeiwen.com/subject/mjcdoctx.html