承接上文《高速缓存模型及MESI协议》可知,MESI协议解决了缓存一致性问题,即多个处理器的高速缓存之间的通信问题,本文将继续讲解目前针对该交互过程的优化以及带来的问题。
一、背景
MESI协议虽然解决了缓存一致性问题,但是本身也存在一定的弱点,比如处理器在执行写操作时,当前处理器需要接收到其他处理器回复的Invalidate Ack消息才能将更新后的内容写入高速缓存。为了规避这种需要等待其他处理器结果的延迟,因此引入了写缓冲器(Store Buffer)和无效化队列(Invalidate Queue)(每个处理器中都有)。
二、概念介绍及优化介绍
(1)、写缓冲器
写缓冲区是处理器内部比高速缓存还小对的高速存储部件,可存储若干个缓存条目(定义可参见文章一),写缓冲区每个处理器都有一个,且不可互相访问。
引入写缓冲区之后,处理器在进行写操作时会做如下处理,刚好回忆下上篇文章的MESI:
情况一、若当前处理器的缓存条目状态为E或者M,则直接将数据写入缓存行(先变化状态再操作)
情况二、若当前处理器的缓存条目状态为S,则需要发送Invalidate消息
情况三、若当前处理器的缓存条目状态为I,则需要发送Read Invalidate消息
针对情况一一般是不需要处理的(某些处理器可能写操作必须进入写缓冲器,x86);情况二、情况三两种处理下,如果当前处理器没有做性能优化,那么当前处理器必须等待其他处理器回复的消息才能执行相应的写操作,这样会浪费诸多时间。
因此引入写缓冲器,这样当前处理器在写操作时,无须等待其他处理器回复的Invalidate消息,可直接将当前处理器的更新结果写入写缓冲器,就可以表示当前写操作已经完成可以继续处理其他指令,当所有处理器回复Invalidate消息时,便将写缓冲器的数据刷新到缓存行中。
(2)、无效化队列
若当前处理器接收到Invalidate消息,正常情况下会将当前处理器缓存条目中的副本数据删除,然后回复Invalidate ACK消息,引入无效化队列之后,当前处理器可先将消息存入无效化队列直接回复Invalidate Ack消息,从而减少了其他处理器写操作所需要的等待时间(某些处理器可能没有引入无效化队列,x86)。无效化队列里面的消息会定时被处理,具体的处理机制目前我还没具体明白,希望哪位大神知道的话可以解释下,多谢。
(3)、存储转发
引入写缓冲器之后,若某处理器在执行写操作之后(数据先写入写缓冲器),又立即进行了读操作,那么此时处理器不能根据内存地址直接读取缓存条目中的数据,因为写缓冲器中的数据可能还没更新。因此存储转发的过程就是读操作会先查询下写缓冲器,如果查询到直接返回,若查询不到则到缓存条目中
以上确实可以给处理器带来性能提升,但是可能带来其他问题
三、内存重排序、可见性问题
上章描述优化策略,本章主要描述该优化带来的一些问题,直接以案例可便于理解:
案例一(写缓冲器):
X、Y为共享变量,初始值为0,R1、R2为局部变量。
当Processor0执行到L2,Processor1执行完S3时,此时S3写操作Processor1可能将更新结果写入写缓冲器,没有更新到高速缓存中,L2读取的数据可能是高速缓存中的旧值0,同样S1写操作Processor0可能将更新结果写入写缓冲器,没有更新到高速缓存中,S4读取的数据可能是高速缓存中的旧值0
这边可介绍下重排序:当Processor1执行到S4时,由于S1操作的写缓冲器缘故,S4感知到Processor0执行了L2操作并没有执行S1操作,即导致了S1操作被重排序到L2之后
案例二(无效化队列):
假设data、ready为共享变量,初始值为0和false,假设Processor0高速缓存中存有变量data和ready副本,Processor1高速缓存中存有变量data副本,没有变量ready副本
(1)、Processor0执行S1,发出Invalidate消息,并将结果写入写缓冲器
(2)、Processor1收到Invalidate消息将消息存入无效化队列并回复Invalidate Ack消息
(3)、Processor0接收到Invalidate Ack消息,将S1操作的结果写入高速缓存,执行S2,因为只有Processor0中存在ready副本,直接写入高速缓存
(4)、Processor1执行L3,由于Processor1中不存有ready副本,因此发送Read消息
(5)、Processor0接收Read消息,并回复
(6)、Processor1接收Read Response消息,L3循环结束
(7)、Processor1执行L4,由于S1操作针对data发送的Invalidate消息,Processor1接收的消息可能还在无效化队列中,此时缓存条目中可能依然存在旧的副本,因此L4可能打印一个旧值
案例三(存储转发):
假设Processor0在t1时刻更新了某个变量值,然后在t2时刻读取了该变量,但是在t1到t2的时间范围内其他的处理器更新了该值,然而Processor0在t1时刻更改之后的值在写缓冲器中,则会导致在t2读取该值的时候直接从写缓冲器中读取,读取不到最新的值。
结束语:
本文主要介绍针对MESI协议消息交互的优化以及带来的问题,下面将讲述如何通过内存屏障来解决这些问题以及一些关键字实现的原理。
谢谢大家阅读,请大家多多指教文章中的不足,互相学习!!
••X、Y为共享变量,初始值为0,R1,R2为局部变量X、Y为共享变量,初始值为0,R1,R2为局部变量