0%

CPU多级缓存-缓存一致性

2018年4月1日 上午11:34


由于main mamory和cache的速度差异被拉大,所以加入了多级缓存。



参考:

  1. 【4】Java并发编程:多线程中的缓存一致性和CAS - CSDN博客

  2. 慕课网高并发实战(二)-并发基础 - 简书

  3. 他是个协议,MESI这个协议,为了保证多个CPU缓存中共享数据的一致性

  4. 定义了cache的四种状态

  5. CPU对cache的四种操作可能会产生不一致的状态

  6. 因此缓存控制器,监听到本地操作和远程操作的时候。需要对cache状态做出一定的修改,从而保证数据在多个缓存之间流转的一致性

  7. SEMI其实是四个状态的缩写。

四种cache状态:2*2=4(是否独有 * 是否与内存一致)

  1. M:Modified被修改的。
    1. 处于这一状态的数据,只在本CPU中有缓存数据,而其他CPU中没有。
    2. 同时其状态相对于内存中的值来说,是已经被修改的,且没有更新到内存中。
  2. E:Exclusive独占的。
    1. 处于这一状态的数据,只有在本CPU中有缓存,
    2. 且其数据没有修改,即与内存中一致。
  3. S:Share共享的。
    1. 处于这一状态的数据在多个CPU中都有缓存
    2. 且与内存一致。
  4. I:Invalid 无效的。
    1. 本CPU中的这份缓存已经无效。

这里首先介绍该协议约定的缓存上对应的监听:

  1. 自己的状态受别的CPU影响
    1. 一个处于M状态的缓存行
      1. 必须时刻监听,所有试图读取该缓存行对应的主存地址的操作
      2. 如果监听到,则必须在此操作执行前把其缓存行中的数据写回CPU
    2. 一个处于S状态的缓存行
      1. 必须时刻监听,使该缓存行无效或者独享该缓存行的请求
      2. 如果监听到,则必须把其缓存行状态设置为I
    3. 一个处于E状态的缓存行
      1. 必须时刻监听,其他试图读取该缓存行对应的主存地址的操作
      2. 如果监听到,则必须把其缓存行状态设置为S
  2. 当CPU需要读取数据时
    1. 一个invalid的缓存必须从主存中读取(变成S或者E状态)来满足该CPU的读请求
    2. 如果不是I,则可以直接读取缓存中的值,
      1. 但在此之前,必须要等待其他CPU的监听结果
      2. 如其他CPU也有该数据的缓存且状态是M,则需要等待其把缓存更新到内存之后,再读取。
  3. 当CPU需要写数据
    1. 只有在其缓存行是M或者E的时候才能执行
    2. 否则需要发出特殊的RFO指令(Read Or Ownership,这是一种总线事务),通知其他CPU置缓存无效(I),这种情况下会性能开销是相对较大的。
    3. 在写入完成后,修改其缓存状态为M
    4. 所以如果一个变量在某段时间只被一个线程频繁地修改,则使用其内部缓存就完全可以办到,不涉及到总线事务,
    5. 如果缓存一会被这个CPU独占、一会被那个CPU 独占,这时才会不断产生RFO指令影响到并发性能。
    6. 这里说的缓存频繁被独占并不是指线程越多越容易触发,而是这里的CPU协调机制,这有点类似于有时多线程并不一定提高效率,原因是线程挂起、调度的开销比执行任务的开销还要大,这里的多CPU也是一样,如果在CPU间调度不合理,也会形成RFO指令的开销比任务开销还要大。当然,这不是编程者需要考虑的事,操作系统会有相应的内存地址的相关判断,这不在本文的讨论范围之内。
  4. 并非所有情况都会使用缓存一致性的,如被操作的数据不能被缓存在CPU内部或操作数据跨越多个缓存行(状态无法标识),则处理器会调用总线锁定;另外当CPU不支持缓存锁定时,自然也只能用总线锁定了,比如说奔腾486以及更老的CPU。