5.3. CPU多级缓存

5.3.1. CPU缓存基本介绍

1)CPU缓存出现的原因
CPU的频率太快,快到主存跟不上,这样在处理器时钟周期内,CPU经常需要等待主存,浪费资源。所以缓存的出现,是为了缓解CPU和内存间速度的不匹配问题。(结论:CPU>缓存>主存)
2)CPU缓存的意义
1)时间局部性:如果某个数据被访问,那么在不久的将来它很有可能会被再次访问。
2)空间局部性:如果某个数据被访问,那么与它相邻的数据很快也能被访问。
3)多核系统中的缓存设置意义
在典型的多核系统中,每一个核心都会有自己的缓存来共享主存总线,每一个CPU都会发出读写请求,而缓存的目的就是为了减少CPU读写共享主存的次数。

5.3.2. 缓存行

1)缓存行的定义
缓存是由缓存行组成的,CPU读取缓存都是以缓存行的形式读取,通常是 64 字节(常用处理器的缓存行是 64 字节的,比较旧的处理器缓存行是 32 字节),并且它有效地引用主内存中的一块地址。在程序运行的过程中,缓存每次更新都从主内存中加载连续的 64 个字节。因此,如果访问一个 long 类型的数组时,当数组中的一个值被加载到缓存中时,另外 7 个元素也会被加载到缓存中。

注:a. 一个 Java 的 long 类型是 8 字节,因此在一个缓存行中可以存 8 个 long 类型的变量。b.如果使用的数据结构中的项在内存中不是彼此相邻的,比如链表,那么将得不到免费缓存加载带来的好处。
2)伪共享问题
a.定义:当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享问题。
b.样例介绍:设想如果有个 long 类型的变量 a,它不是数组的一部分,而是一个单独的变量,并且还有另外一个 long 类型的变量 b 紧挨着它,那么当加载 a 的时候将免费加载 b。看起来似乎没有什么毛病,但是如果一个 CPU 核心的线程在对 a 进行修改,另一个 CPU 核心的线程却在对 b 进行读取。当前者修改 a 时,会把 a 和 b 同时加载到前者核心的缓存行中,更新完 a 后其它所有包含 a 的缓存行都将失效,因为其它缓存中的 a 不是最新值了。而当后者读取 b 时,发现这个缓存行已经失效了,需要从主内存中重新加载。缓存都是以缓存行作为一个单位来处理的,所以失效 a 的缓存的同时,也会把 b 失效,反之亦然。这样就出现了一个问题,b 和 a 完全不相干,每次却要因为 a 的更新需要从主内存重新读取,它被缓存未命中给拖慢了。

c.解决方案
以解决两个long类型变量的伪缓存问题为例:
方法1:由于一个缓存行是 64 个字节,一个 long 类型是 8 个字节,所以可以在两个 long 类型的变量之间再加 7 个 long 类型,如下所示:

class Pointer {volatile long x;long p1, p2, p3, p4, p5, p6, p7;volatile long y;
}

方法2:重新创建自己的 long 类型,而不是 java 自带的 long:

class Pointer {MyLong x = new MyLong();MyLong y = new MyLong();
}class MyLong {volatile long value;long p1, p2, p3, p4, p5, p6, p7;
}

方法3:使用 @sun.misc.Contended 注解(java8),默认使用这个注解是无效的,需要在JVM启动参数加上-XX:-RestrictContended才会生效。

@sun.misc.Contended
class MyLong {volatile long value;
}

注意,避免伪共享的主要思路就是让不相干的变量不要出现在同一个缓存行中,以上三种方式中的前两种是通过加字段的形式实现的,加的字段又没有地方使用,可能会被jvm优化掉,所以建议使用第三种方式。

3)缓存行的优势
使用缓存行能够有效提高程序运行效率,尤其是处理数组。

5.3.3. 缓存一致性(MESI)

1)缓存一致性的核心思想
保证了每个缓存中使用的共享变量的副本是一致的。核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
CPU、Cache、缓存一致性协议、主存之间的关系如下图所示:

2)缓存行的状态
缓存行的状态可分为四类:Modified 被修改状态、 Exlusive独享状态、 Share共享状态、 Invalid无效状态。

a.Modified (被修改)
该缓存行只被缓存在该CPU的缓存中且是被修改过的,因此其与主存中的数据是不一致的。该缓存行的内存需要在未来的某个时间点写回主存,这个时间点是允许其他CPU读取主存中相应的内存之前,但该状态的值被写回主存后,该缓存行的状态就会变成独享状态。
b.Exlusive(独享)
该缓存行只被缓存在该CPU的缓存中且是未被修改过的,与主存中的数据一致,此种状态可以在任何时刻,在有其他CPU读取该内存时变成共享状态。同样地,当有CPU修改该缓存行内容时,该状态可以变成被修改状态。
c.Share(共享)
该缓存行可能被多个CPU进行缓存,且各缓存中的数据与主存中的数据是一致的,但某个CPU修改该缓存行时,其他CPU存储的该缓存行就会被作废,变成无效状态。
Invalid(无效)
该缓存行可能被其他CPU修改。
3) 对CPU缓存的四种操作
Local read: 读本地缓存中的数据
Local write: 将数据写入本地的缓存中
Remote read: 将内存的数据读取过来
Remote write: 将数据写回到主存中
4) 四种缓存状态的相互转换
一个缓存,除了Invalid状态外,都可以满足CPU的读请求(Local read和Remote read)。
一个写请求只有在该缓存行是Modified或者Exlusive状态时才能被执行,如果当前缓存行处于Share状态,则必须先将该缓存行变成无效的状态,这个操作通常通过广播的方式来完成,此时既不允许不同的CPU同时修改同一个缓存行,即使修改该缓存行不同位置的数据也不允许。
处于Modified状态的缓存行必须时刻监听所有试图读该缓存行相对主存的操作,这种操作必须在CPU将该缓存写回到主存并将状态变为Share状态之后才可以执行,之前必须要延迟执行。
一个处于Share状态的缓存行必须监听其他缓存使该缓存行无效或者独享该缓存行的请求,并将缓存行变成无效。
处于Exlusive状态的缓存行要监听其他缓存读该缓存中缓存行的操作,一旦有操作,需要将其变为Share状态,因此对于Modified和Exlusive,数据总是精确的,它们和缓存行的真正状态是一致的。
Share状态下各缓存行中的数据可能是非一致的,如果一个缓存将处于Share状态的缓存行作废了,另一个缓存可能已经独享了该缓存行,但该缓存却不会将该缓存行升迁为Exlusive状态,这是因为其他缓存不会广播它们作废掉该缓存行的通知,同样,由于缓存并没有保存该缓存行复制的数量,也没有办法确定自身是否已经独享了该缓存行。从这点来看,Exlusive状态更像一种投机性优化,因为如果CPU想修改一个处于Share状态的缓存行,总线需要将所有该缓存行复制的值变成Invalid状态才行,而修改Exlusive状态却不需要总线事务,因为处于Exlusive状态时就仅有该缓存拥有该缓存行。

运行时内存-CPU多级缓存相关推荐

  1. 并发编程-02并发基础CPU多级缓存和Java内存模型JMM

    文章目录 CPU多级缓存 CPU多级缓存概述 CPU 多级缓存-缓存一致性协议MESI CPU 多级缓存-乱序执行优化-重排序 JAVA内存模型 (JMM) 计算机硬件架构简易图示 JAVA内存模型与 ...

  2. 【并发编程】CPU多级缓存与缓存一致性

    CPU多级缓存与缓存一致性 定义 cpu缓存是位于CPU与内存之间的临时存储器,它的容量比内存小的多,但是交换速度却比内存要快得多 为什么需要CPU cache? cpu的频率太快了,快到主存跟不上, ...

  3. [转]JVM运行时内存结构

    [转]http://www.cnblogs.com/dolphin0520/p/3783345.html 目录[-] 1.为什么会有年轻代 2.年轻代中的GC 3.一个对象的这一辈子 4.有关年轻代的 ...

  4. 运行时常量池在哪里_Java虚拟机详解(二)------运行时内存结构

    首先通过一张图了解 Java程序的执行流程: 我们编写好的Java源代码程序,通过Java编译器javac编译成Java虚拟机识别的class文件(字节码文件),然后由 JVM 中的类加载器加载编译生 ...

  5. 【Android 逆向】Android 逆向基本概念 ( 软件运行时内存结构 | 文件与内存之间的联系 )

    文章目录 一.Android 软件运行时内存结构 二.Android 文件与内存之间的联系 一.Android 软件运行时内存结构 Android 软件运行时内存结构 : 硬件层 : 内存中 , 硬件 ...

  6. 运行时错误7内存溢出_JVM运行时内存数据区域

    阅读本文大概需要5分钟 作者:AI乔治出处:https://my.oschina.net/u/3611782/blog/4530512 1 讨论背景 周志明老师写的<深入理解Java虚拟机> ...

  7. 深入理解java虚拟机一 JAVA运行时内存区域与class文件

    一 JAVA运行时内存区域 JVM在加载class文件时,会将class文件定义的数据结构转为运行时内存中的数据,那么jvm是如何安排运行时的内存区域呢? jvm将运行时内存划分为以下几个部分: 堆: ...

  8. Java运行时内存工作过程

    介绍在Java8中,运行时内存是如何工作的,对象的走向如何,Java7和Java8内存模型的变化又是什么?接下来让我给你解开谜团 运行时内存 基本组成如图: 其中新生代又分为Eden区.Survivo ...

  9. JVM运行时内存概念-堆栈及新生代、老年代、持久代

    JVM内存有一些按不同标准划分的概念,这里来理清一下. 一.首先java虚拟机规范定义了java运行时数据区在概念上应该有的分区,这是抽象概念不对应也不限制物理上的具体实现,不同的虚拟机可以有不同的实 ...

最新文章

  1. 清华《摸鱼学导论》开班啦!1000多学子在线摸鱼,无期末考试
  2. 软件自动升级ftp服务器,国人自己的专业FTP服务器软件(上)
  3. 在JavaScript里写类层次结构?别那么做!
  4. Linux zip 加密压缩
  5. createbitmap导致的内存泄漏如何处理_C++ 如何避免内存泄漏,一篇就够
  6. python做些什么项目_Python 的练手项目有哪些值得推荐
  7. 以下是ECMAScript 2016、2017和2018中所有新增功能的示例
  8. P2237 [USACO14FEB]自动完成Auto-complete
  9. 重磅福利 | 全网唯一,多年踩坑经验,探索测试策略新奇方法之案例剖析(一)...
  10. vue template 复用_vue-组件基础
  11. LoadRunner10自带的WEBTOURS,无法显示Flights页面问题解决办法
  12. PIX防火墙基本特性:失效处理机制和冗余-原理与实验
  13. (转)5分钟APIG实战: 使用Rust语言快速构建API能力开放
  14. 【Python画图】不显示x、y坐标
  15. 直接在文件夹打开cmd
  16. IIS 6 UrlRewrite 步骤
  17. Android 解决OutOfMemory,从避免内存溢出开始
  18. stm32 ADC hal库实现
  19. 路透社:韩国游戏巨头Nexon欲出售控股权 腾讯将扮演关键角色
  20. android获取各运营商信号,一篇关于 Android 获取运营商的全面笔记

热门文章

  1. Google Adwords API
  2. 电脑杀蚊器:超级蚊霸+电子蚊香
  3. Wireshark数据抓包还原文件
  4. 南大计算机系统基础实验 ics2020,pa0
  5. 抖音”命名的方法论:解码字节跳动如何A/B测试
  6. WakeData完成数千万人民币A轮融资,由红杉资本中国基金领投
  7. 来自腾讯的76款开源项目
  8. 【数字化】PLM视角下,数字化工厂的构建之道!
  9. 怎么用虚拟机玩王者荣耀?(VMware安装的win11并完成下载安卓应用的配置教程)
  10. Ozmosis实现BIOS直接启动Yosemite,基本完美