一文读懂从 CPU 多级缓存 & 缓存一致性协议(MESI)到 Java 内存模型

这篇具有很好参考价值的文章主要介绍了一文读懂从 CPU 多级缓存 & 缓存一致性协议(MESI)到 Java 内存模型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

CPU 多级缓存 & 缓存一致性协议(MESI)

CPU 多级缓存

  • 参考:Java Memory Model

缓存一致性协议(MESI)

  • 多级缓存的出现解决了CPU处理速度和内存读取速度不一致的问题,但是同时也带来缓存不一致的问题,为了解决这个问题,我们引入了缓存一致性协议,常见的缓存一致性协议有MSI,MESI,MOSI,Synapse,Firefly及DragonProtocol等等,下文以MESI协议进行讲述。

一文读懂从 CPU 多级缓存 & 缓存一致性协议(MESI)到 Java 内存模型,Java 后端经典面试题,JVM,操作系统,缓存,java,开发语言

缓存行(Cache line)

  • 缓存行是指在缓存中的最小数据单元。

四种缓存状态

  • 缓存行有4个状态,用2个bit表示。
状态 描述 监听任务
E 独享 该Cache line有效,数据被修改,和内存数据一致,数据只存在本Cahe中 必须监听所有试图读该缓存行的操作,操作必须在该缓存行写回主存并将状态变为S后执行
M 修改 该Cache line有效,数据被修改,和内存数据不一致,数据只存在本Cahe中 必须监听所有试图读该缓存行的操作,操作必须在该缓存行写回主存并将状态变为S后执行
S 共享 该Cache line有效,数据和内存数据一致,数据存在多个Cache中 必须监听其它缓存使该缓存无效或独享该缓存的请求,并将该缓存行变为无效
I 失效 该Cache line无效
  • 注:对于M和E状态而言总是精确的,他们在和该缓存行的真正状态是一致的,而S状态可能是非一致的。如果一个处于S状态的缓存失效,另外一个缓存行可能已经独享了该缓存行,但是不会升迁为独享状态,因为失效并不会广播给其它缓存行。

缓存行状态转换

一文读懂从 CPU 多级缓存 & 缓存一致性协议(MESI)到 Java 内存模型,Java 后端经典面试题,JVM,操作系统,缓存,java,开发语言

多核协同示例

一文读懂从 CPU 多级缓存 & 缓存一致性协议(MESI)到 Java 内存模型,Java 后端经典面试题,JVM,操作系统,缓存,java,开发语言

  • 初始状态:CPUB 存在缓存变量 X 状态为 M
  • CPUA 发出指令读取 X 指令,通过 bus 读取 X,检测到地址冲突,将 CPUB 缓存变量状态置为 S,读取 X 到 CPUA 完成
  • 此时,CPUB 修改缓存变量并通过 bus 写回主存,发现地址冲突,将 CPUA 中的变量从 S 状态置为 I,数据写回主存
网站体验
  • 模拟一致性的整个过程:https://www.scss.tcd.ie/Jeremy.Jones/VivioJS/caches/MESIHelp.htm

MESI优化和引入的问题

  • 在上述多核CPU为保证缓存一致性进行协同的过程中,消息传递的时间远远大于CPU执行时间,如果每次的操作都需要等待协同指令响应完成,那么就会大大降低处理器的处理性能,因此引入了Store Bufferes和Invalidate Queue进行优化。
Store Bufferes & Invalidate Queue
  • 从上述的多核协同案例中我们可以发现,每次修改缓存中的元素,都需要将无效状态指令(Invalidate Acknowledge)执行完才能将修改的数据写回缓存行中,等待协同指令会造成CPU运算能力浪费,因此,Store Bufferes被引入,我们不需要等待协同指令返回就可以将修改的数据写入Store Bufferes,当再次读取时若在Store Bufferes中已存在直接从Buffer中读取(称为“Store Forwarding”),只有当收到所有协同指令响应后才能写回缓存行中。
  • Store Bufferes 是有限的,因此当要写回缓存行时为了更快的得到所有Invalidate Acknowledge指令的响应,实际上也不会立即执行,而是放入了Invalidate Queue中,并立即返回响应,在合适的时机去执行。

一文读懂从 CPU 多级缓存 & 缓存一致性协议(MESI)到 Java 内存模型,Java 后端经典面试题,JVM,操作系统,缓存,java,开发语言

Store Bufferes & Invalidate Queue 带来的问题
  • Store buffer 什么时候写回并没有保证
value = 3;

void exeToCPUA(){
  value = 10;
  isFinsh = true;
}

void exeToCPUB(){
  if(isFinsh){
    // value 一定等于10?
    // 如果 Store Bufferes 没有写回那么将导致数据不一致
    assert value == 10;
  }
}
  • Invalidate Acknowledge 什么时候执行没有保证
// 当一个CPU尝试读取实际已经失效但未执行 Invalidate Acknowledge 的数据时,会导致数据不一致

硬件内存模型

  • 由于 Store Bufferes & Invalidate Queue 的引入,导致 Store Bufferes 写入缓存行和执行 Invalidate Acknowledge 的时机需要十分合适才能尽可能释放CPU的处理能力,实际上CPU并不知道什么时候会执行,因此将这个任务留给了写程序的人,这就是我们常说的内存屏障。
读屏障 & 写屏障
  • 写屏障 Store Memory Barrier(a.k.a. ST, SMB, smp_wmb)是一条告诉处理器在执行这之后的指令之前,应用所有已经在Store buffer中的保存的指令到缓存行中。

  • 读屏障Load Memory Barrier (a.k.a. LD, RMB, smp_rmb)是一条告诉处理器在执行任何的加载前,应用所有已经在失效队列中的失效操作的指令。文章来源地址https://www.toymoban.com/news/detail-743166.html

void executedOnCpu0() {
    value = 10;
    // 在更新数据之前必须将所有存储缓存(store buffer)中的指令执行完毕。
    storeMemoryBarrier();
    finished = true;
}
void executedOnCpu1() {
    while(!finished);
    // 在读取之前将所有失效队列中关于该数据的指令执行完毕。
    loadMemoryBarrier();
    assert value == 10;
}

思考 & 联系

  • 不同的系统架构有不同的内存屏障,以X86架构为例:读屏障:lfence、写屏障:sfence、读写屏障:mfence。
  • MESI 缓存一致性协议中为了尽可能的提高性能,引入了 Store Bufferes & Invalidate Queue ,将数据具体的失效时机和写入时间交给了内存屏障控制,而 JMM 则基于内存屏障保证数据的可见性。
  • volatile 关键字底层使用了LOCK关键字,LOCK关键字的本质是锁(总线锁或缓存行锁),只是LOCK关键字的一部分能力具备和内存屏障相同的作用,但是和内存屏障还是有一定区别。

到了这里,关于一文读懂从 CPU 多级缓存 & 缓存一致性协议(MESI)到 Java 内存模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 缓存数据一致性探究

    缓存是一种较低成本提升系统性能的方式,自它面世第一天起就备受广大开发者的喜爱。然而正如《人月神话》中的那句经典的“没有银弹”中所说,软件工程的设计没有银弹。 就像每一次发布上线修复问题的同时,也极易引入新的问题,自缓存诞生的第一天起, 缓存与数

    2024年02月16日
    浏览(33)
  • 【Redis】缓存一致性

    读缓存 双检加锁 策略 采用 双检加锁 策略 多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它。 其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。 后面的线程进来发现已经有缓存了,

    2023年04月24日
    浏览(55)
  • 深入理解高并发下的MySQL与Redis缓存一致性问题(增删改查数据缓存的一致性、Canal、分布式系统CAP定理、BASE理论、强、弱一致性、顺序、线性、因果、最终一致性)

    一些小型项目,或极少有并发的项目,这些策略在无并发情况下,不会有什么问题。 读数据策略:有缓存则读缓存,然后接口返回。没有缓存,查询出数据,载入缓存,然后接口返回。 写数据策略:数据发生了变动,先删除缓存,再更新数据,等下次读取的时候载入缓存,

    2024年03月20日
    浏览(46)
  • Redis之缓存一致性

    按照缓存更新的方式大致分为: 内存淘汰、过期删除、主动更新。 利用 Redis 的内存淘汰策略,当内存不足时自动进行淘汰部分数据,下次查询时更新缓存,一致性差,无维护成本。 因为 Redis 是基于内存的,如果内存超过限定值( Redis 配置文件的 maxmemory 参数决定 Redis 最大内

    2024年02月07日
    浏览(48)
  • 【Redis】之数说缓存一致性

    对于使用 Redis 作为缓存来说,如何保证数据库和缓存数据一致性是个麻烦的问题。对于缓存和数据库的操作,主要有以下两种方式: 先删缓存,再更新数据库; 先更新数据库,再删除缓存; 这两种方式都存在缓存一致性问题,下面我们就分析一下如何解决这两种方式的缓存

    2024年02月13日
    浏览(38)
  • 缓存和数据库一致性

    项目的难点是如何保证缓存和数据库的一致性。无论我们是先更新数据库,后更新缓存还是先更新数据库,然后删除缓存,在并发场景之下,仍然会存在数据不一致的情况(也存在删除失败的情况,删除失败可以使用异步重试解决)。有一种解决方法是延迟双删的策略,先删

    2024年01月17日
    浏览(42)
  • Redis缓存双写一致性

    如果redis中有数据:需要和数据库中的值相同 如果redis中无数据:数据库中的值要是最新值,且准备回写redis 缓存按照操作来分,可细分为两种: 只读缓存和读写缓存 只读缓存很简单:就是Redis只做查询,有就是有,没有就是没有,不会再进一步访问MySQL,不再需要会写机制

    2023年04月17日
    浏览(41)
  • Redis缓存(双写一致性问题)

    前言 : 什么是缓存? 缓存就像自行车,越野车的避震器 举个例子:越野车,山地自行车,都拥有\\\"避震器\\\", 防止 车体加速后因惯性,在酷似\\\"U\\\"字母的地形上飞跃,硬着陆导致的 损害 ,像个弹簧一样; 同样,实际开发中,系统也需要\\\"避震器\\\",防止过高的数据访问猛冲系统,导致其操作线程无法

    2024年02月02日
    浏览(59)
  • Redis---缓存双写一致性

    目录 一、什么是缓存双写一致性呢?  1.1 双检加锁机制  二、数据库和缓存一致性的更新策略 2.1、先更新数据库,后更新缓存  2.2 、先更新缓存,后更新数据库  2.3、先删除缓存,在更新数据库 延时双删的策略:  2.4.先更新数据库,在删除缓存(常用) 2.5、实际中是不可

    2024年02月15日
    浏览(43)
  • 28.Netty源码之缓存一致性协议

    Mpsc 的全称是 Multi Producer Single Consumer,多生产者单消费者。 Mpsc Queue 可以保证多个生产者同时访问队列是线程安全的,而且同一时刻只允许一个消费者从队列中读取数据。 Netty Reactor 线程中任务队列 taskQueue 必须满足多个生产者可以同时提交任务,所以 JCTools 提供的 Mpsc Queu

    2024年02月13日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包