Disruptor的共享与缓存是怎样的

技术Disruptor的共享与缓存是怎样的这篇文章将为大家详细讲解有关Disruptor的共享与缓存是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。什么是共享下图是

本文将详细解释如何共享和缓存中断器。文章内容质量很高,我就分享给大家作为参考。希望大家看完这篇文章后对相关知识有一定的了解。

00-1010下图是计算的基本结构。L1、L2、L3分别代表一级缓存、二级缓存和三级缓存。越靠近CPU的缓存,速度越快,容量越小。因此,L1缓存虽小但速度快,而且离使用它的CPU内核很近。L2更大更慢,仍然只能由单个中央处理器内核使用。L3更大、更慢,由单个插槽上的所有CPU内核共享;最后是主存,由所有插槽中的所有CPU内核共享。

Disruptor的共享与缓存是怎样的

图3计算机CPU和缓存示意图。

当中央处理器执行操作时,它首先去L1查找所需的数据,然后去L2,然后去L3。如果这些缓存最终都不可用,所需的数据将进入主内存。你走得越远,计算的时间就越长。因此,如果您非常频繁地执行某项操作,您应该尝试确保数据在L1缓存中。

另外,线程之间共享一条数据时,一个线程需要将数据写回主存,而另一个线程需要访问主存中对应的数据。

以下是从CPU访问不同级别数据的时间概念:

从CPU到所需CPU周期所需的时间约为60-80nsQPI总线传输(套接字之间,未画出),约20nsL3缓存,约40-45周期,约15nsL2缓存,约10周期,约3nsL1缓存,约3-4周期,约1ns寄存器,1周期。可以看出,CPU读取主存中的数据会比L1慢近两个数量级。

什么是共享

缓存由许多缓存行组成。每个高速缓存行通常是64字节,它实际上是指主存储器中的一个地址块。一个Java长变量是8字节,所以一个缓存行可以存储8个长变量。

每次中央处理器从主存储器中提取数据时,它也会将相邻的数据存储到同一高速缓存行中。

当访问长数组时,如果数组中的一个值被加载到缓存中,它将自动加载其他七个值。所以你可以非常快速地遍历这个数组。事实上,您可以非常快速地遍历在连续内存块中分配的任何数据结构。

以下示例测试使用缓存线和不使用缓存线的要素之间的效果比较。

package com . meituan . Falssharing;

/**

*@authorgongming

* @描述

*@date16/6/4

*/

publicclassCacheLineEffect{

//考虑一般缓存行大小为64字节,长类型占用8字节。

static long[][]arr;

publicationstativitmain(String[]args){ 0

arr=new long[1024 * 1024][];

for(inti=0;i1024 * 1024I){ 0

arr[I]=new long[8];

(=NationalBureauofStandards)国家标准局

p;for (int j = 0; j < 8; j++) {
                arr[i][j] = 0L;
            }
        }
        long sum = 0L;
        long marked = System.currentTimeMillis();
        for (int i = 0; i < 1024 * 1024; i+=1) {
            for(int j =0; j< 8;j++){
                sum = arr[i][j];
            }
        }
        System.out.println("Loop times:" + (System.currentTimeMillis() - marked) + "ms");
 
        marked = System.currentTimeMillis();
        for (int i = 0; i < 8; i+=1) {
            for(int j =0; j< 1024 * 1024;j++){
                sum = arr[j][i];
            }
        }
        System.out.println("Loop times:" + (System.currentTimeMillis() - marked) + "ms");
    }
}

在2G Hz、2核、8G内存的运行环境中测试,速度差一倍。

结果: Loop times:30ms Loop times:65ms

什么是伪共享

ArrayBlockingQueue有三个成员变量: - takeIndex:需要被取走的元素下标 - putIndex:可被元素插入的位置的下标 - count:队列中元素的数量

这三个变量很容易放到一个缓存行中,但是之间修改没有太多的关联。所以每次修改,都会使之前缓存的数据失效,从而不能完全达到共享的效果。

Disruptor的共享与缓存是怎样的

图4 ArrayBlockingQueue伪共享示意图

如上图所示,当生产者线程put一个元素到ArrayBlockingQueue时,putIndex会修改,从而导致消费者线程的缓存中的缓存行无效,需要从主存中重新读取。

这种无法充分使用缓存行特性的现象,称为伪共享。

对于伪共享,一般的解决方案是,增大数组元素的间隔使得由不同线程存取的元素位于不同的缓存行上,以空间换时间。

package com.meituan.FalseSharing;
 
public class FalseSharing implements Runnable{
        public final static long ITERATIONS = 500L * 1000L * 100L;
        private int arrayIndex = 0;
 
        private static ValuePadding[] longs;
        public FalseSharing(final int arrayIndex) {
            this.arrayIndex = arrayIndex;
        }
 
        public static void main(final String[] args) throws Exception {
            for(int i=1;i<10;i++){
                System.gc();
                final long start = System.currentTimeMillis();
                runTest(i);
                System.out.println("Thread num "+i+" duration = " + (System.currentTimeMillis() - start));
            }
 
        }
 
        private static void runTest(int NUM_THREADS) throws InterruptedException {
            Thread[] threads = new Thread[NUM_THREADS];
            longs = new ValuePadding[NUM_THREADS];
            for (int i = 0; i < longs.length; i++) {
                longs[i] = new ValuePadding();
            }
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(new FalseSharing(i));
            }
 
            for (Thread t : threads) {
                t.start();
            }
 
            for (Thread t : threads) {
                t.join();
            }
        }
 
        public void run() {
            long i = ITERATIONS + 1;
            while (0 != --i) {
                longs[arrayIndex].value = 0L;
            }
        }
 
        public final static class ValuePadding {
            protected long p1, p2, p3, p4, p5, p6, p7;
            protected volatile long value = 0L;
            protected long p9, p10, p11, p12, p13, p14;
            protected long p15;
        }
        public final static class ValueNoPadding {
            // protected long p1, p2, p3, p4, p5, p6, p7;
            protected volatile long value = 0L;
            // protected long p9, p10, p11, p12, p13, p14, p15;
        }
}

在2G Hz,2核,8G内存, jdk 1.7.0_45 的运行环境下,使用了共享机制比没有使用共享机制,速度快了4倍左右。

结果: Thread num 1 duration = 447 Thread num 2 duration = 463 Thread num 3 duration = 454 Thread num 4 duration = 464 Thread num 5 duration = 561 Thread num 6 duration = 606 Thread num 7 duration = 684 Thread num 8 duration = 870 Thread num 9 duration = 823

把代码中ValuePadding都替换为ValueNoPadding后的结果: Thread num 1 duration = 446 Thread num 2 duration = 2549 Thread num 3 duration = 2898 Thread num 4 duration = 3931 Thread num 5 duration = 4716 Thread num 6 duration = 5424 Thread num 7 duration = 4868 Thread num 8 duration = 4595 Thread num 9 duration = 4540

备注:在jdk1.8中,有专门的注解@Contended来避免伪共享,更优雅地解决问题。

关于Disruptor的共享与缓存是怎样的就分享到这里了,希望

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/39236.html

(0)

相关推荐

  • Hive中静态分区与动态分区的示例分析

    技术Hive中静态分区与动态分区的示例分析这篇文章给大家分享的是有关Hive中静态分区与动态分区的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。  分区是hive存放数据的一种方式。将

    攻略 2021年12月10日
  • 化妆品牌子大全,日本有哪些好用的化妆品牌

    技术化妆品牌子大全,日本有哪些好用的化妆品牌日本女生从初中开始很多就开始化妆了化妆品牌子大全,所以整体国家的化妆品还是发展的很不错的,毕竟需求大嘛,而且从贵妇到大众品牌一应俱全,很多大集团都会出不同价位的品牌,以满足不同

    生活 2021年11月1日
  • 编写CSS的方法有哪些

    技术编写CSS的方法有哪些这篇文章主要讲解了“编写CSS的方法有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“编写CSS的方法有哪些”吧!  CSS正在改变网站设计的

    攻略 2021年12月10日
  • qt画切片地图(qt 地图演示)

    技术Qt编写地图综合应用之如何绘制雨量分布这篇文章主要介绍Qt编写地图综合应用之如何绘制雨量分布,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、前言雨量分布图是在区域地图基础上,针对区域中的每个

    攻略 2021年12月25日
  • centos7 安装包安装mysql8.0.18 rpm-bundle

    技术centos7 安装包安装mysql8.0.18 rpm-bundle centos7 安装包安装mysql8.0.18 rpm-bundle一、资源地址
    下载地址https://dev.mysql

    礼包 2021年11月4日
  • mysql修改时区的方法是什么

    技术mysql修改时区的方法是什么本篇内容介绍了“mysql修改时区的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有

    2021年12月2日