共享内存架构中的缓存一致性

15 篇文章 0 订阅
10 篇文章 0 订阅

摘自曼彻斯特大学伊恩·沃森的讲座

概述

  • 我们已经讨论过在单核上的性能优化
    • 局部性
    • 向量
  • 现在让我们优化一个共享内存程序
  • 两种架构:
    • 基于总线的共享内存机器(小规模)
    • 基于目录的共享内存机器(大规模)

基于总线共享内存的体制

基本情况很简单:
1

体制

  • 总线通常是简单物理连接(导线)
  • 总线带宽限制了cpu数量
  • 可能是多个内存元素
  • 当前,假定每个cpu仅有一级缓存

内存一致性面临的问题

  • 假定仅一级缓存与主内存
  • 处理器写入它缓存的位置
  • 其他缓存可能保存了共享的副本-副本将要过期
  • 仅仅更新主内存是不够的

案例

2

处理器1读取X:从内存中读取X值为24并缓存它
处理器2读取X:从内存中读取X值为24并缓存它
处理器1写入X值32:仅更新了它的本地缓存副本
处理器3读取X:应该读取到什么值呢?
内存与处理器2认为X值为24
处理器1认为X值为32
注意:写入缓存是不够好的

总线嗅探

  • 每一个cpu(缓存系统)‘嗅探’(即:不断的观察)它缓存数据地址相关的写动作
  • 假定总线结构是全局的,即所有的通信都对所有处理器可见
  • 更具扩展性的解决方案是:‘基于目录’的一致性方案

嗅探协议

  • 写无效
    • cpu想要写一个地址,抢占一个总线周期并发送一个‘写无效’消息
    • 所有处理器嗅探到缓存‘写无效’消息后,将他们副本相应的缓存行置为无效
    • cpu写入它缓存副本(将定现在它也写入了内存)
    • 其他cpu的任何共享读现在都将miss,并重新获取新数据
  • 写更新
    • 想要写数据的cpu抢占一个总线周期,并在更新自己副本时广播新数据
    • 所有嗅探缓存的处理器更新他们的副本
  • 注意在这两种方案中,同时写的问题都由总线仲裁解决 - 同一时间仅有一个cpu可以使用总线

更新或无效

  • 更新看起来最简单,最明显,最快,但是:
    • 对同一个数据(字)多次写入(无中间读)仅需要一个无效消息,但是需要对每个监听消息的处理器更新
    • 写入(通常)多字缓存块中的同一块只需要一个invalidate,但需要多次更新。
  • 由于空间与时间都具有局部性,上述的场景经常发生
  • 在共享内存多处理器中,总线带宽是一种宝贵的资源
  • 经验表明,invalidate协议使用的带宽要少得多相对于写更新
  • 将只考虑invalidate方案的实现细节

实现问题

  • 在这两种方案中,如果知道缓存值未共享(在另一个缓存中复制)可以避免发送任何消息
  • invalidate表示认为缓存值更新已写入内存。如果我们使用一个‘copy back’方案,其他处理器可能再次获取到旧值在缓存miss情况下。
  • 我们需要一个协议来处理所有的这些问题

MESI协议

  • 一个实用的多处理器invalidate协议,它试图最小化实用总线
  • 允许实用‘write back’方案-即在‘dirty’缓存行被替换之前主存没有更新
  • 常规缓存标签的扩展,即无效标签与‘dirty’标签在普通回写缓存中。

缓存行可能是四种状态之一(2bits)

  • 已变更 - 缓存行已经被修改,与主存不一致 - 仅存在当前缓存副本(多处理器‘dirty’)

  • 独有的 - 缓存行与主存一致,并且仅存在当前缓存副本

  • 共享的 - 与主存一致,但是副本也存在其他缓存中

  • 无效的 - 缓存行是无效的(如果在普通缓存中)

  • 缓存行状态随着内存访问事件的变化而变化

  • 事件可以是

    • 由本地处理器动作引起的(即缓存访问)
    • 由总线动作引起的 - 作为一次嗅探的结果
  • 缓存行只有在地址匹配时才受自己状态的影响

  • 操作可以非正式的描述,看作本地处理器的动作

    • 读命中
    • 读未命中
    • 写命中
    • 写未命中
  • 可以通过状态转换图更正式的描述

MESI本地读命中

  • 缓存行状态一定是MES三者之一
  • 读命中的值一定是正确的本地值(如果是M状态,它一定被本地修改了)
  • 简单的返回值
  • 没有状态变更

MESI本地读未命中

  • 没有其他缓存副本

    • 处理器通过总线请求主存
    • 主存的value读至本地缓存,标记状态为E
  • 已经存在E状态副本

    • 处理器通过总线请求主存
    • 嗅探缓存将值放入总线
    • 主存访问被抛弃
    • 本地处理器缓存值
    • 二者的缓存行状态至为S
  • 多个缓存已经有了S状态副本

    • 处理器通过总线请求主存
    • 嗅探缓存之一将值放入总线(总线仲裁)
    • 主存访问被抛弃
    • 本地处理器缓存值
    • 本地副本设置为S状态
    • 其他副本保持S状态
  • 一个缓存副本状态为M

    • 处理器通过总线请求主存
    • 嗅探缓存将值放入总线
    • 主存访问被抛弃
    • 本地处理器缓存值
    • 本地副本标记为S
    • 原值(M)复制回主存
    • 原值状态变更M->S

MESI本地写命中

缓存行必须是MES状态之一

  • M
    • 缓存行是独有的,并且已经为‘脏’行
    • 更新本地缓存值
    • 没有状态变更
  • E
    • 更新本地缓存值
    • 状态变更E->M
  • S
    • 处理器在总线上广播一个invalidate消息
    • 拥有副本状态为S的嗅探处理器变更状态S->I
    • 本地缓存值更新
    • 本地状态变更S->M

MESI本地写未命中

具体操作取决于其他处理器中的副本

  • 没有其他副本
    • 从主存中读取值至本地缓存(?)
    • 更新值
    • 本地副本状态变更为M
  • 存在其他副本,存在一个副本状态为E,或者多个副本状态为S
    • 从主存中读取值至本地缓存,总线事务标记为RWITM(read with intent to modify)
    • 嗅探处理器看到该动作则将他们的本地副本置为无效状态I
    • 本地副本更新并变更状态为M

另一个副本状态为M

  • 处理器发布总线事务标记为RWITM
  • 嗅探处理器看到该动作
    • 阻塞RWITM请求
    • 获取总线控制权
    • 将它的副本写回主存
    • 设置它的副本状态为无效I
  • 原本地处理器再次发布RWITM请求
  • 当前是简单的无副本场景
    • 从主存读取值至本地缓存
    • 本地副本值更新
    • 本地副本状态变更为M

把所有动作放在一起

  • 上述所有信息可以用一个简洁的状态变更图表达
  • 图展示了缓存行在处理器中发生了什么,由于
    • 由处理器产生的主存访问(读命中/未命中,写命中/未命中)
    • 由其他处理器进行的内存访问会导致由这个snoopy缓存观察到的总线事务(主存读,RWITM,Invalidate)

MESI – 本地启动的访问

3

MESI – 远端启动的访问

4

MESI 注意事项

  • 有一些少数的变种(写未命中场景的特殊做法)
  • 如果缓存行状态为M,那么当缓存线被逐出时,正常的“回写”就完成了
  • 多级缓存
    • 如果缓存是包含的,那么只有最低级别的缓存需要在总线上窥探

目录方案(Directory Schemes)

  • 嗅探方案不能扩展,因为它们依赖与广播
  • 基于目录的方案允许扩展
    • avoid broadcasts by keeping track of all PEs caching a memory block, and then using point-to-point messages to maintain coherence
    • they allow the flexibility to use any scalable point-to-point network

5

关键问题

  • 内存与目录带宽的扩展
    • 不能集中主存或目录内存
    • 需要一个分布式内存和目录结构
  • 目录内存需求不能很好的扩展
    • Number of presence bits grows with number of PEs
    • Many ways to get around this problem
      • limited pointer schemes of many flavors
  • 行业标准
    • SCI: Scalable Coherent Interface,可扩展的一致性接口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值