首页 > 代码库 > Store Forwarding

Store Forwarding

        上一文中讲到,由于新值存在于 Store Buffer 中,从 Cache line 中取得旧值,进而引起错误的问题,让我们来进一步解决这个问题。

        在 <<走进 CPU 的 Cache>> 一文中有一张现代 CPU 的结构图,部分如下:

     +--------------+             
     |     CPU0     |                  
     +--------------+                 
        ^         |               
        |         |                  
        |         V                     
        |     +--------+             
        |<--> | Store  |              
        |     | Buffer |                
        |     +--------+           
        |         |                 
        |         V                
     +--------------+         
     |     Cache    |     
     +--------------+ 

        在 CPU 载入内存和 Store Buffer 之间有一个交互的过程,这就是 Store forwaring,根据上一文中讲到的, 当 CPU0 执行 b = a + 1,时,此时它要加载 a 的值,此时它发现在 store buffer 中有 a 的值,
它就会直接从 store buffer 中读取,然后进行计算,这样,最后得到的 b 的值就将得到正确的结果了。
        仿佛世界顿时美好了起来,还有没有更好的优化方法呢,仔细回想一下,Store Buffer 的空间也是有限的,如果这样的情况发生的太多,在 cache miss 的情况下会经常有,那么一旦 store buffer 满了,后
面的指令依然要像上一文中那样等待,一直到其它 CPU 回应之后,再从 Store Buffer 中应用到 Cache 中,如果其它 CPU 忙碌,或者其它 CPU 接收到 read validate 消息后,置自己的 cache line 失效也会造成大量
的时间浪费,要是收到 read validate 消息后,直接给一个回应,然后自己在合适的时机慢慢处理这些消息就好了。
        对了,我只能说设计 CPU 的那些家伙太厉害了,他们发明了另一个东西,Invalidate Queue, 这个队列会将 Invalidate 消息缓存起来,然后马上给一个 Invalidate Acknowledge 消息,在结构图中也展示了这
一个队列。那么这个问题就完美解决了。
        让我们再来看一个小例子。 a 和 b 初始化都为 0。 a 在 CPU1 的 cache 中, b 在 CPU0 的 cache 中。
        CPU 0 执行下面的代码:
        a = 1;
        b = 1;
        CPU1 执行下面的代码:
        while (b != 1);
        assert (a == 1);
        断言一定能成功吗? 让我们假设下面的情况发生:
        1. CPU0 执行 a = 1, 但是发现 a 不在 cache 中,它就发出 "read invalidate" 消息,并且把 a 的值存在 store buffer 中;然后执行 b = 1;
        2。CPU1 执行 b != 1 的比较,读取 b 的值,结果不在 cache 中,因为它不想改写,所以只发一个 "read" 消息出去;
        3. CPU0 把 1 直接写入到 b 的 cache line 中, 并收到 "read" 消息,设 cache line 为共享,并给出回应。
        4. CPU1 收到 b 的值,退出循环,执行断言,断言失败;
        5. CPU1 收到 "read invalidate" 消息,但此时已晚。
        通过上面的例子,我们可以看出,由于 CPU 间消息时序的不确定性,还有 store buffer 存储写入操作的延迟性,一起导致了这个问题,这也是一些处理器普遍会存在的问题,尤其是一些弱内存序的 CPU。
        所以 Memory barriers 呼之欲出。
        Memroy barriers ,即内存屏障,它会将 Store Buffer 中的所有项目,都标打上标记,这样,当再有 store 操作时,如果发现 store buffer 中在项被标记,即使在 cache line 中已存在,它也不会直接写
入 cahce line ,取而代之的是,该操作,这里是 b = 1,也会被存入 Store Buffer, 并且该项不作标记,一直等到 Store Buffer 中被标记的项全部被应用到 Cache line 中,这些操作才会被正常应用。看来 Memory 
barriers 真是个好东西。
        (待续)

Store Forwarding