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)

相关推荐

  • Docker原理、架构与应用是什么

    技术Docker原理、架构与应用是什么本篇内容主要讲解“Docker原理、架构与应用是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Docker原理、架构与应用是什么”吧

    攻略 2021年12月13日
  • 如何用OAuth 2.0实现权限认证mybatis连接mysql数据库

    技术如何用OAuth 2.0实现权限认证mybatis连接mysql数据库本篇内容介绍了“如何用OAuth 2.0实现权限认证mybatis连接mysql数据库”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困

    攻略 2021年12月4日
  • 数字化转型

    技术数字化转型 数字化转型 - 快速试错 - 思维整理互联网访客-前端免费文章导流-吸引受众-导师背景介绍-增强受众信服力-导流入私域群流量池(弱连接)-初级课程过滤白嫖客户-筛选优质客户-线下付费课进

    礼包 2021年12月6日
  • React Router 5.1.0如何使用useHistory实现页面跳转导航

    技术React Router 5.1.0如何使用useHistory实现页面跳转导航React Router 5.1.0如何使用useHistory实现页面跳转导航,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希

    攻略 2021年11月12日
  • 开源分布式图数据库有哪些(最牛的分布式数据库)

    技术开源分布式数据库RadonDB的核心技术与实现是怎样的开源分布式数据库RadonDB的核心技术与实现是怎样的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,

    攻略 2021年12月18日
  • 【Mybatis】单独使用mybatis的SQL模板解析

    技术【Mybatis】单独使用mybatis的SQL模板解析 【Mybatis】单独使用mybatis的SQL模板解析前言
    由于公司的项目历史设计问题坑多不见底,新项目没时间改,旧项目改不动。生产存在非

    礼包 2021年11月30日