1. 为何需要cache一致性
访问memory数据的速度相比core的运行速度来说,要花费更多的时钟周期,为了减轻这个差异引进了存储器层次结构,如图1所示。在层次结构中,越往上,读写速度越快,价格更贵,存储容量也越小。
图1 存储器结构层次
随着摩尔定律的发展,人们试图增加CPU的core数以提升系统整体性能,这类系统称之为多core系统。一个包含多core的系统中,每个core可能都有自己的私有cache,并且有可能会对相同的memory地址进行修改。如果多个core同时对同一memory地址进行读写操作,则很可能会导致cache内容不一致,从而导致数据不一致,最终造成系统出错和崩溃。因此,在设计多core系统时,必须考虑cache一致性(cache coherency)问题来确保数据的正确性和系统的稳定性。举个例子,假设2个core的系统,每个core私有的L1 cache的cacheline大小是64Bytes。两个core都读取0x80地址数据,导致0x80开始的64Bytes数据都分别加载到core0和core1的私有L1 cache中。
图2 两core系统读取0x80地址的数据
core0对0x80执行写操作,写入值为0x01。Core0的私有L1 cache更新对应cacheline的值。然后,core1读取0x80的数据,core1发现命中自己私有L1 cache,然后返回0x00值,并不是core0写入的0x01值,这就造成了core0和core1的私有L1 cache数据不一致现象。
图3 core0更新0x80地址的数据
按照正确的处理流程,可以使用以下两种方法保证多core cache的一致性:
- Core0修改0x80地址数据的时候,除了更新core0的私有cache之外,还应将修改的数据通知到core1的私有cache去更新0x80地址的数据。
- Core0修改0x80地址数据的时候,除了更新core0的私有cache之外,还应通知core1的私有cache将0x80地址所在的cacheline置为无效。保证core1读取数据时不会命中自己的cache。这样core1就必须重新从core0或memory中去读取0x80地址最新的数据。
Cache一致性可以通过软件和硬件来解决,通常来说,软件维护cache一致性维护成本太高,会导致性能下降。现在的实现方案基本都是使用硬件来自动维护多核cache的一致性,并且对软件和程序员来说是透明的。因此,本文只介绍硬件维护cache一致性。硬件维护cache一致性需要制定一致性协议,一致性协议是由系统内的各个分布式硬件参与者组件共同实现和维护的一组规则。一致性协议有许多版本,Arm的ACE(AXI Coherency Extensions)和CHI(Coherent Hub Interface),AMD的Coherent HyperTransport(HT),Intel的QuickPath Interconnect(QPI)等等。
2. 一致性协议的本质
虽然一致性协议众多,但本质上讲,所有一致性协议都通过将写入的数据传播到所有一致性cache来使一个core的写入对其它core可见。具体来说有两点原则:
- Single-Writer, Multiple-Read(SWMR):在任意时刻,对于任意的memory地址A,只有一个core可以写它(也可以读它),或者多个core读它。因此,永远不会有这样的情况:一个core可以写入memory地址A,而其它core可以同时读取或写入该memory地址A。如下图4所示,可以把时间会切割为无限小的切片,在每个切片中,对于memory地址A,要么单个core具有读写访问权限,要么若干core(可能为0)具有只读访问权限。比如T1和T4时刻,允许多个core对同一个memory进行读取,但在T2和T3种ÿ