对称式共享存储器系统支持共享数据和私有数据的缓存。

  • 私有数据被单个处理器使用。

    好处:把一个私有数据从存储器缓存到Cache之后,对该数据的访问就可以在Cache中进行,因此减少了平均访存时间和对存储器带宽的要求。

    同时因为没有其他处理器使用这些数据,程序的行为与单处理器系统相同。

  • 共享数据被多个处理器所使用。

    通过读写共享数据完成处理器之间的通信。

    好处:共享数据装载到Cache中时,会在多个Cache中形成副本,这样1. 一方面会减少访问时间并降低对存储器带宽的要求,2. 还可以减少多个处理器同时读共享数据时所产生的冲突。

    但是,共享数据进入Cache也产生了一个新问题——Cache的一致性问题。

一、什么是多处理器的Cache一致性?

存储器一致性定义一

一个模糊且简单的定义:如果在一个存储器系统中读取任何一个数据项的返回结果总是最近写入的数值,那么就可以认为该存储器具有一致性。(定义一)

这个定义包含了两个方面:一方面是一致(coherence),它定义了读操作可以返回什么样的数值【what】;另一方面是连贯(consistency),它定义了写入的数值什么时候才能被读操作返回【when】。

一致定义了对同一个存储器地址进行的读写操作行为;连贯定义了关于访问其他存储器地址的读写操作。

存储器一致性定义二

如果一个存储器系统满足以下条件,那么认为该存储器系统是一致的:(定义二)

  1. 处理器P对地址X的写操作后面紧跟着处理器P对X的读操作,而且在这次读操作和写操作之间没有其他处理器对X进行写操作,这时读操作总是返回P写入的数值。

    这个性质保证了程序的顺序,即使在单处理器中也要保证这个性质。

  2. 在其他处理器对X的写操作后,处理器P对X执行读操作,这两个操作之间有足够的间隔并且没有其他处理器对X进行写操作,这是,读操作返回的是写入的数值。

    这个性质给出了一致性的概念,如果一个处理器对某个数据执行读操作时,总是的读入旧的数据,那么该存储器是非一致的。

  3. 对同一地址的写操作是串行执行的;也就是说,任何两个处理器对同一地址的两个写操作在所有处理器看来都有相同的顺序。例如,对同一地址先后写入数值1和数值2,处理器绝不会从该地址中先读出2再读出1。

这个性质称为写串行化,保证同一地址所写的顺序对任何处理器来说都是相同的。

在三个条件之外,还又两个假设:

假设:

  1. 直到所有处理器都看到了写操作之后一个写操作才算完成,并且后续的写操作才能开始。
  2. 处理器不会因为其他存储操作而改变写操作的顺序。

这两个假设意味着如果处理器向地址A写入后又向地址B写入,所有能看到B中新值的处理器必须也能看到A的新值。

定义一和定义二的联系

定义二的三个条件已经体现了定义一的一致方面【what】,定义一的连贯方面,即什么时候才能获得写进去的值【when】,则体现在条件2的读写操作之间有足够的时间间隔上面。

通常不可能要求在一个处理器写入X的数值后,其他处理器就能即刻在X上读出这一值。因此,如果一个处理器对X进行写后,很短时间内另一处理器对X进行读,那么无法保证该读操作能返回写入的数值,因为这一刻写入的数据是怎知可能还没被处理器发送出去。

总的来说,缓存一致性机制需要解决的问题就是 2 点:

  • 特性 1 - 写传播(Write Propagation): 每个 CPU 核心的写入操作,需要传播到其他 CPU 核心;
  • 特性 2 - 写事务串行化(Transaction Serialization): 各个 CPU 核心所有写入操作的顺序,在所有 CPU 核心看起来是一致。

如果没有写串行化,举个例子:假如 CPU 有 4 个核心,Core 1 将共享数据修改为 1000,随后 Core 2 将共享数据修改为 2000。在写传播下,“修改为 1000” 和 “修改为 2000” 两个事务会同步到 Core 3 和 Core 4。但是,如果没有事务串行化,不同核心收到的事务顺序可能是不同的,最终数据还是不一致。

二、实现一致性的方案

在一致的多处理机中,Cache提供了共享数据的迁移和复制功能。

共享数据的迁移是把远程的共享数据项备份放在本处理器局部的Cache中使用,从而降低了对远程共享数据的访问延迟。

共享数据的复制是把多个处理器需要同时读取的共享数据项的备份放在各自的局部Cache中使用。

对多个处理器维护一致性的协议称为Cache一致性协议(Cache-coherent Protocal)

目录协议与监听协议

(1)监听(Snooping)——每个Cache除了包含物理存储器中块的数据备份之外,也保存着每个块的共享状态信息。Cache通常连在共享存储器的总线上,各个Cache控制器通过监听总线来判断他们是否有总线上请求的数据块。【每个数据块状态是分散在各个Cache中的】

对于监听协议,常用于集中式共享存储器体系结构,因为可以利用已有的物理连接(总线)来进行广播,但因为广播带来的带宽压力大,所以监听协议的可扩展性较差。

(2)目录(Directory)——物理存储器中共享数据块的状态及相关信息均被保存集中地在一个称为目录的地方。【每个数据块状态是集中在目录中的】

对于目录协议,常用于分布式共享存储器体系结构,通过目录避免了广播操作,减小了带宽负担,可扩展性好。

实现Cache一致性协议的关键是跟踪共享数据块的状态。目前有两类协议,采用了不同的共享数据状态跟踪技术:

写作废协议与写更新协议

可通过两种方法来维持一致性要求:

(1)写作废协议(Write Invalidate)——在一个处理器写某个数据项之前保证它对该数据项有唯一的访问权,唯一的访问权保证了在进行写后不存在其他可读或可写的备份,因为别的备份都作废了。又叫写无效。

(2)写更新协议(Write Update)——当一个处理器写某数据项时。通过广播使其他Cache中所有对应的该数据项备份进行更新。又叫写广播。

由于写更新协议必须将所有写操作广播给共享Cache,需要更大的带宽,所以大多处理器都选择写作废协议。

三、监听协议

由于监听协议需要广播,对总线带宽的需求大,因此仅适用于小规模的多处理机,即集中式共享存储器系统。

监听协议的关键是利用总线或其他广播媒介进行作废操作。当某个处理器进行写数据时,必须先获得总线的控制权,然后将要作废的数据块的地址放在总线上。其他处理器一致监听总线,他们检测地址所对应的数据是否在它们的Cache中,若在,则作废相应数据块。

当写Cache未命中时,除了作废其他处理器上相应的Cache数据块以外,还要从存储器取出该数据块。

  • 对于写直达Cache,因为所有写的数据同时被写回主存,则从主存中总可以取到最新的数据值。

  • 对于写回Cache,因为最新之可能在某个处理器的Cache中,也可能在主存中,所以得到数据的最新值会困难一些。在写回Cache失效时可使用相同的监听机制:1. 当请求处理器的Cache发生写失效后,广播该数据块的地址。2. 其他处理器都监听放在总线上的地址,如果某个处理器发现它含有被请求数据块的一个已经修改过的备份(即修改过了该数据块但还未写回内存),它九江这个数据块送给发出读请求的处理器,并停止其对主存的访问请求。

相比于写直达Cache,写回Cache实现一致性有两个缺点:

  1. 实现复杂度显然更高
  2. 写回Cache从处理器Cache中重新找回数据块的时间通常比写直达Cache从共享存储器中找回数据块的时间长

但写回Cache所需的存储器带宽较低的特点使得其在多处理机实现上很受欢迎。

写回Cache条件下的监听协议实现

利用总线实现写传播和写事务串行化:

  • 写传播 - 总线嗅探: 总线除了能在一个主模块和一个从模块之间传输数据,还支持一个主模块对多个从模块写入数据,这种操作就是广播。要实现写传播,其实就是将所有的读写操作广播到所有 CPU 核心,而其它 CPU 核心时刻监听总线上的广播,再修改本地的数据;
  • 事务串行化 - 总线仲裁: 总线的独占性要求同一时刻最多只有一个主模块占用总线,天然地会将所有核心对内存的读写操作串行化。如果多个核心同时发起总线事务,此时总线仲裁单元会对竞争做出仲裁,未获胜的事务只能等待获胜的事务处理完成后才能执行。

使用的辅助结构

  • 有效位

    在写回Cache的数据块中,有效位被用于指示该块是否有效。利用有效位,可以使作废的处理很简单,只需要将该位置为无效即可。

  • 状态位

    为了分辨某个数据块所处的状态,还要给每个块增加一个特殊的状态位。

  • 降低冲突

    因为每次总线任务均要检查Cache的地址位,这可能与CPU对Cache的访问冲突,可通过以下两种技术之一降低冲突:

    1. 复制标志位:将Cache的标志位复制一份,一份正常用于Cache访问,另一份用于监听,两个任务可以并行对标志位进行读。但代价就是修改标志位的时候也必须同时修改两份,同时如果两个任务修改标志位冲突,则非抢先者将被挂起。
    2. 多级包含Cache:采用多级Cache,通常为两级,靠近CPU的第一级Cache是较远的第二级Cache的一个子集。于是,监听可针对第二级Cache进行,而处理器的大多数访问针对第一季Cache,极大地避免了冲突。但是,如果监听命中第二级Cache,它必须垄断对各级Cache的访问,更新块状态并可能写回数据,这通常要挂起处理器对Cache的访问。

    可采用将第二级Cache中的标志位复制,会更有效地减少CPU和监听之间的冲突。

    判断数据块是否共享,可以帮助写操作判断是否需要发送作废操作——当对共享数据块进行写操作时,Cache会在总线上发送一个作废操作,并把该块标记为专有(非共享);当对专有数据块进行写操作时,由于只有该Cache有数据块的唯一副本,所以不用发送作废操作到总线上了。这样避免了发送作废操作,可以节省时间和带宽。

一致性协议本质上是一个状态机,具有若干状态。不同的协议具有不同的状态组合和转换策略。

对于数据块状态的描述,可以用以下四个特征来编码:

  • 有效性(Validity):有效的块含有数据的最新值,可以被读。
  • 肮脏性(Dirtiness):如果一个Cache中的块与在内存中对应的块是不同的,那么意味着Cache中的块是被修改过的、最新的、有效的,还未被写入内存,而内存中的对应块是旧的、无效的。因为Cache中的块被修改了,所以称其为“脏”的。
  • 独占性(Exclusivity):如果缓存块是系统中该块的唯一私有缓存副本,则该缓存块是独占的。(它也可能出现在内存中,但不可能出现在其他Cache中)。
  • 所有权(Ownership):如果缓存控制器(或内存控制器)负责响应对该块的一致性请求,那么它就是该块的所有者。

稳态

一致性协议常见稳定状态:

  • 共享状态(S)是指块是同于主存的,该块可能还在其他Cache中,也可能仅在该Cache中
  • 独占状态(E)是指块是同于主存的,仅在该Cache中
  • 修改状态(M)是指块是不同与主存的,即在Cache中已经被更新;修改状态暗示了此时的块已经被独占,即仅在该Cache中
  • 无效状态(I)是指块里的数据是旧的,已经失效了

NB:

“同于主存”是指在Cache中的块与在内存中对应的块是相同的。有时也称是“干净”的

“不同于主存”是指在Cache中的块与在内存中对应的块是不同的,内存中的块是旧的、无效的。有时也称是“脏”的

M (Modified):该块是有效的、排他的、拥有的,并且可能是脏的。 该块可以被读取或写入。 缓存具有块的唯一有效副本,缓存必须响应对块的请求,并且 LLC/内存中的块副本可能是陈旧的。

S (Shared):该块有效但不排他,不脏,不拥有。缓存具有块的只读副本。其他缓存可能具有该块的有效只读副本。

I (Invalid):块无效。缓存要么不包含块,要么包含可能无法读取或写入的陈旧副本。在本入门书中,我们不区分这两种情况,尽管有时前一种情况可以表示为“不存在”状态。

O (Owned):该块是有效的,拥有的,并且可能是脏的,但不是独占的。 缓存具有块的只读副本,并且必须响应对该块的请求。 其他缓存可能具有该块的只读副本,但它们不是所有者。LLC/内存中的块副本可能已过时。

E (Exclusive):该块是有效的、排他的和干净的。缓存具有块的只读副本。没有其他缓存拥有该块的有效副本,并且 LLC/内存中的块副本是最新的。在本入门书中,我们认为当区块处于独占状态时,它是拥有的,尽管在某些协议中独占状态不被视为所有权状态。当我们在后面的章节中介绍 MESI 监听和目录协议时,我们将讨论是否把独占块视为所有者的问题。

监听一致性协议一般通过每个节点的有效状态控制器来实现,控制器对每个来自处理器总线的请求做出响应,然后改变响应Cache块的状态

MSI协议

MSI协议是最基础的监听协议实现。有无效共享修改三种状态

MESI协议

MESI是最经典的监听协议。有无效共享独占修改三种状态

相比于MSI协议,MESI协议增加了独占(E)状态,是一种投机性优化:如果一个CPU想修改一个处于S状态的缓存行,总线事务需要将所有该缓存行的copy变成invalid状态,而修改E状态的缓存不需要使用总线事务。

演示MESI协议网页VivioJS MESI (tcd.ie)

监听协议和集中式共享存储器体系结构的局限性

随着多处理器中处理器数目的增加,或者处理器对存储器带宽的增加,系统的任何集中式资源都会成为“瓶颈”

例如在基于总线的多处理器中,总线必须同时支持由于Cache导致的存储器通信和一致性通信。如果是只有一个物理存储器的集中式共享存储器体系结构,总线的带宽(bandwidth)负担会极大。

为了增加处理器和存储器之间的通信带宽,设计者使用多种总线以及各种互联网络,如交叉开关和小型点对点网络。在该设计中,存储器系统可以被配置称多个物理组,有效增加带宽。这正是集中式共享存储器和分布式共享存储器的结合

由上例可知,使用监听Cache一致性协议可以不要求使用集中式总线这样的很容易造成带宽瓶颈的通信方式,但仍然要求完成广播。由于一致性通信量与处理器速度没有关系,这种一致性通信限制了处理器的扩展与速度

四、目录协议

对于监听协议,在处理每个Cache缺失时,都需要和所有的Cache进行通信,造成带宽瓶颈,限制了处理器的扩展。

每个目录负责跟踪共享本地存储器的Cache,存储器的每一块在目录中对应有一项

每个目录项主要有“状态”和“位向量”两种成分。状态描述该目录所对应的存储块的当前情况;位向量有处理器数量的位数,其每一位对应于一个处理器的局部Cache,用于指出该Cache中有无该存储块的备份。


本文是对《计算机体系结构——量化研究方法(第四版)》和《计算机体系结构(第二版)》的相关章节的整理

参考链接🔗

cache之多核一致性(一) - 总线上没有秘密 - 知乎 (zhihu.com)