Cache一致性协议

在说伪共享问题之前,有必要聊一聊什么是Cache一致性协议

局部性原理

时间局部性:如果一个信息项正在被访问,那么在近期它很可能还会被再次访问

比如循环、方法的反复调用等

空间局部性:如果一个存储器的位置被引用,那么将来他附近的位置也会被引用

比如顺序结构、数组

Cache的作用

CPU在摩尔定律的指导下以每18个月翻一番的速度在发展,然而内存和硬盘的发展速度远远不及CPU。为了解决这个问题,CPU厂商在CPU中内置了少量的高速缓存Cache,以解决访存速度和CPU运算速度之间不匹配的问题

带Cache的CPU访存过程

CPU和Cache交换数据以为单位。Cache与主存以为单位,一个缓存行(Cache Line)对应一个主存块

  • Cache命中,则直接从Cache中读取数据
  • Cache不命中,则访问主存,并将一个主存块调入Cache中,存入为一个缓存行。这个过程中可能由于Cache满而发生替换,替换算法包括RAND、FIFO、LRU、LFU

  • Cache命中时

    • 写回法(write back):CPU只将数据写入Cache,只有当数据调出Cache时,才写入主存
    • 写穿法(write through):CPU同时将数据写入Cache和主存
  • Cache不命中时
    • 写分配法:从主存中将数据块调入Cache,并修改Cache,和写回法配合使用
    • 非写分配法:只写入主存,不调入Cache,和写穿法配合使用

Cache和主存的映射方式(三种):直接映射、全相联映射、组相联映射

如果是单CPU结构,这么执行没有其他问题。但是现代系统往往包含多个CPU,每个CPU都有各自的Cache。多核CPU的情况下有多个一级缓存,如何保证缓存内部数据的一致性,不让系统数据混乱。这里就引出了Cache一致性协议——MESI。注意,这并不是唯一的缓存一致性协议,还有其他协议如MOSEI(相对于MESI多引入了一个Owned状态,并重新定义了S状态),这里不多介绍

MESI协议详解

MESI(Modified Exclusive Shared Or Invalid),也称伊利诺斯协议,是一种广泛使用的、支持写回策略的缓存一致性协议。MESI协议其实就是使用4种状态来标记各个缓存行(Cache Line)的状态,而这些状态英文首字母缩写就构成了“MESI”

MESI协议中的各种状态

每个缓存行都使用一个状态来标记,该状态总共有4种,使用2bit进行存储:

  • M(Modified):相应的数据只被缓存在该CPU的Cache中,但数据是被修改过的(脏数据),即与主存中的数据不一致。该缓存行中的内存需要在未来的某个时间点,但必须是其它CPU读取主存中相应内存之前,将数据写回主存
  • E(Exclusive):相应的数据只被缓存在该CPU的缓存中,数据是未被修改过的,与主存中的数据一致
  • S(Shared):相应的数据被多个CPU缓存,且各个CPU的Cache中的数据和主存都是一致的
  • I(Invalid):该缓存行中的数据是无效的,因为有其他CPU修改了数据

总线嗅探机制(监听)

每个CPU都可以感知其他CPU的行为,比如读、写某个缓存行,这就是嗅探机制,也称监听。所有的缓存行(除了Invalid状态)都需要监听自己和其他CPU对相应的缓存行的读写操作,也称触发事件,从而根据触发事件和自身状态,进行状态的转换

各种触发事件

触发事件 描述
本地读取(Local Read) 本CPU读取本Cache的数据
本地写入(Local Write) 本CPU向本Cache写入数据
远端读取(Remote Read) 其他CPU读取它们各自Cache的数据
远端写入(Remote Write) 其他CPU向它们各自Cache写入数据

MESI中各个状态之间的转换

下图描述了当前缓存行在不同触发事件下的状态切换:

下表是对上图的一个详细解释:

举例

假设CPU0、CPU1、CPU2、CPU3中有一个缓存行(包含变量x)都是S状态

此时CPU1要对变量x进行写操作,这时候通过总线嗅探机制,CPU0、CPU2、CPU3中的缓存行会置为I状态(无效),然后给CPU1发响应,收到全部响应后CPU1会完成对变量x的写操作,并更新CPU1内的缓存行为M状态,但不会将数据x同步到主存中

接着CPU0想要对变量x执行读操作,却发现本地缓存行是I状态,就会触发CPU1去把缓存行写回到主存中,然后CPU0再去主存中同步最新的值

其他一些细节

1、写缓冲

前面的描述隐藏了一些细节,比如实际CPU1在执行写操作,更新缓存行的时候,其实并不会等待其他CPU的状态都置为I状态,才去做些操作,这是一个同步行为,效率很低。当前的CPU都引入了写缓存器技术,也就是在CPU和cache之间又加了一层buffer,在CPU执行写操作时直接向写缓冲写入数据,然后就忙其他事去了,等其他CPU都置为I之后,CPU1才把buffer中的数据写入到缓存行中

2、多级缓存

现代系统都会采用多级缓存架构,L1-L3级缓存,其中L3缓存是所有CPU共享的一个缓存,但MESI的描述中并没有涉及L3缓存。其实上文提到的所有跟“主存”交换数据的地方,在L3缓存存在的情况下,都应该替换为L3缓存。比如我上一节举的例子中,CPU0中某缓存行是I,CPU1 中是M。当CPU0想到执行local read操作时,就会触发CPU1中的缓存写入到主存中,然后CPU0从主存中取最新的缓存行。其实这里的描述是不准确的,因为由于L3缓存的存在,这里其实是直接从L3缓存读取缓存行,而不直接访问主存。个人认为是如果在描述MESI的状态流转时,如果引入L3缓存,会使得描述过于复杂,因此一般的描述都会刻意忽略L3缓存

**



作者:酒冽        出处:https://www.cnblogs.com/frankiedyz/p/15786362.html

版权:本文版权归作者和博客园共有

转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

**

伪共享

由于内存和Cache之间的交换单位是内存块/缓存行,因此如果访问一个变量,会将一整个内存块读入一个缓存行。但是如果多个线程访问的变量不相同,且这些变量在内存中的位置临近,那么很可能在同一个缓存行中。在Cache一致性协议(如MESI协议)的约束下,多个线程(CPU)在并行读写相对应的缓存行会有限制,因此其他线程不得不去访问低级别的Cache甚至是主存,这会导致cache没有起到真正的作用,程序性能下降

伪共享示例

如图,线程1访问变量x,而线程2访问变量y,而这两个变量在内存中的位置临近(在同一个内存块中),虽然线程都将该内存块读入到各自的工作内存(Cache)中,但是在Cache一致性协议的约束下,同一时间两个线程很难自由地读写相同位置的缓存行,那么可能就会让其中一个线程去低级别的内存中读写数据,性能因此降低,这就是伪共享

一般地址连续的多个变量更可能被放在同一个缓存行中,例如创建数组时,数组中的多个元素更可能被放入同一个缓存行中

如何避免伪共享问题

JDK8之前

JDK8之前,使用字节填充的方式,即创建一个变量时,使用填充字段填充该变量所在的缓存行,从而避免多个变量被放入同一个缓存行中,如下:

public final static class FilledLong {
public volatile long value = 0L;
public long p1, p2, p3, p4, p5, p6;
}

一般来说,缓存行为64 Byte,而经过填充的FilledLong对象有7*8 Byte=56 Byte,而FilledLong对象的对象头也有8 Byte,正好填满一个缓存行

JDK8及之后

JDK8提供了一个注解——sun.misc.Contended,用于解决伪共享问题,上述代码可以修改为如下:

@sun.misc.Contended
public final static class FilledLong {
public volatile long value = 0L;
}

Thread类中,也有这样的字段,如下:

@sun.misc.Contended("tlr")
long threadLocalRandomSeed; /** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@sun.misc.Contended("tlr")
int threadLocalRandomProbe; /** Secondary seed isolated from public ThreadLocalRandom sequence */
@sun.misc.Contended("tlr")
int threadLocalRandomSecondarySeed;

但是,@Contended注解只用于Java核心类,而用户类路径下的类使用该注解,需要添加JVM参数-XX:-RestrictContended。填充宽度默认为128 Byte,也可以自定义宽度,通过JVM参数-XX:ContendedPaddingWidth来设定

Cache一致性协议与伪共享问题的更多相关文章

  1. 《大话处理器》Cache一致性协议之MESI (转)

    原文链接:http://blog.csdn.net/muxiqingyang/article/details/6615199 Cache一致性协议之MESI 处理器上有一套完整的协议,来保证Cache ...

  2. Cache一致性协议之MESI

    http://blog.csdn.net/muxiqingyang/article/details/6615199 Cache一致性协议之MESI 处理器上有一套完整的协议,来保证Cache一致性.比 ...

  3. 《大话处理器》Cache一致性协议之MESI【转】

    转自:https://blog.csdn.net/muxiqingyang/article/details/6615199 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载 ...

  4. 简述伪共享和缓存一致性MESI

    什么是伪共享 计算机系统中为了解决主内存与CPU运行速度的差距,在CPU与主内存之间添加了一级或者多级高速缓冲存储器(Cache),这个Cache一般是集成到CPU内部的,所以也叫 CPU Cache ...

  5. Cache Line 伪共享发现与优化

    https://yq.aliyun.com/articles/465504 Cache Line 伪共享发现与优化 作者:吴一昊,杨勇 1. 关于本文 本文基于 Joe Mario 的一篇博客 改编而 ...

  6. C++性能榨汁机之伪共享

    C++性能榨汁机之伪共享 来源  http://irootlee.com/juicer_false_sharing/ 前言 在多核并发编程中,如果将互斥锁的争用比作“性能杀手”的话,那么伪共享则相当于 ...

  7. 3.3.1 Cache一致性的基本概念

    PCI设备对可Cache的存储器空间进行DMA读写的操作的过程较为复杂,有关Cache一致性的话题可以独立成书.而不同的处理器系统使用的Cache Memory的层次结构和访问机制有较大的差异,这部分 ...

  8. 线程基础:多任务处理——MESI协议以及带来的问题:伪共享

    1.概述 本文和后续文章将着眼CPU的工作原理阐述伪共享的解决方法和volatile关键字的应用. 2.复习CPU工作原理2.1.CPU工作原理要清楚理解本文后续内容,就需要首先重新概述一下JVM的内 ...

  9. cache line 伪共享

    https://blog.csdn.net/qq_27680317/article/details/78486220认识CPU Cache CPU Cache概述 随着CPU的频率不断提升,而内存的访 ...

随机推荐

  1. Linux 三剑客之sed

    目录 Linux 三剑客之sed 命令补充: sort命令 uniq命令 cut命令 tr命令 wc命令 三剑客 - sed 编辑模式: 定位分类: 实例如下: d模式--删除模式 p模式--打印 a ...

  2. dart系列之:安全看我,dart中的安全特性null safety

    目录 简介 Non-nullable类型 Nullable List Of Strings 和 List Of Nullable Strings !操作符 late关键字 总结 简介 在Dart 2. ...

  3. Django常用的QuerySet操作

    在这里我根据是否支持链式调用分类进行介绍 1. 支持链式调用的接口 all 使用频率比较高,相当于SELECT * FROM table 语句,用于查询所有数据. filter 使用频率比较高,根据条 ...

  4. ViewModel的创建

    ViewModel的创建 ViewModel本身只是ViewModel这个类的子类: class MainViewModel: ViewModel() { } 在屏幕旋转UI重建的时候, 它是如何拥有 ...

  5. mysql联合查询更新数据库例子

    mysql联合查询更新数据库例子,用户表,部门表,把用户表中的部门属性更新为部门表的主键UPDATE user_table AS utINNER JOIN belongdept AS bd ON bd ...

  6. 出现此错误An association from the table refers to an unmapped class

    出现此错误An association from the table refers to an unmapped class,怎么解决: 把Diaocha.hbm.xml文件路径加入到applicat ...

  7. WPF DataGrid OxyPlot 卡顿优化

    不是优化,我是想用这个标题吸引遇到相同问题的同学过来看看. UI如下,左边DataGrid有7列,右边OxyPlot显示折线图 列表4000+数据,折线图4000+个点,页面卡的用不了. 体现就是列表 ...

  8. SpringBoot简单整合Actuator监控

    pom依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>s ...

  9. 【LeetCode】874. Walking Robot Simulation 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 模拟 日期 题目地址:https://leetcod ...

  10. 洛谷 P3431:[POI2005]AUT-The Bus(离散化+DP+树状数组)

    题目描述 The streets of Byte City form a regular, chessboardlike network - they are either north-south o ...