首页 > 代码库 > 走进 CPU 的 Cache

走进 CPU 的 Cache

    看了上一篇文章,你可能很想知道,为什么程序的运行结果会是这样,现在,就让我们来走进 CPU 的世界。

    在 SMP(对称多处理器)时代,多个 CPU 一起工作,使运算能力进一步提升,那么CPU 是如何协调好内存访问的呢?

     +--------------+                   +--------------+     
     |     CPU0     |                   |     CPU1     |     
     +--------------+                   +--------------+     
        ^         |                       ^         |        
        |         |                       |         |        
        |         V                       |         V        
        |     +--------+                  |     +--------+   
        |<--> | Store  |                  |<--> | Store  |   
        |     | Buffer |                  |     | Buffer |   
        |     +--------+                  |     +--------+   
        |         |                       |         |        
        |         V                       |         V        
     +--------------+                   +--------------+     
     |     Cache    |                   |     Cache    |     
     +--------------+                   +--------------+     
            |                                  |          
            |                                  |                
      +------------+                     +------------+                 
      | Invalidate |                     | Invalidate |         
      |   Queue    |                     |   Queue    |  
      +------------+                     +------------+  
            |            Interconnect          |                   
            +----------------------------------+
                              |
                  +-----------------------+
                  |       Memory          |
                  +-----------------------+
    上图是现代 CPU 普遍采用的架构,由于 CPU 的运算能力的增长速度比内存访问速度快很多,使得内存的存取成为指令执行过程中相对较慢的过程,所以在 CPU 运算单元和
主存之间还会存在多级的缓存,通常称为 L1, L2, L3,对他们的访问速度依次递减,上图中只显示了一级缓存的存在。这样,当 CPU 访问内存时,就会先在自己的 Cache 中查
找,看是否这段内存已经被缓存起来,如果已在缓存中,那么,直接访问缓存即可完成功能。有了缓存的存在,CPU 的运算速度得到了极大的提高。由于缓存比较昂贵,所以一般都较小,在我的奔腾 E5800 的电脑上,一级缓存一共才 128KBytes.

    缓存是由缓存行组成,通常称之为 Cache line,现代 Intel CPU 中,一般都为 64B,通常,缓存都是由多路关联组成,我的 CPU 就是 4-way 关联,这里不想深入讲解多路关
联缓存的工作原理,有兴趣的可以自行查阅。
    当缓存中没有需要的数据时,叫做 cache miss, 这时一般会从内存中加载,cache 加载都是以 cache line 为单位,并且以 cache line 对齐,也就是在 cache line 对齐的
地址上加载 cache line 大小的内容进入缓存,这里一般为 64 个字节,就也就是把常用数据放在一个 cache line 对齐的内存中,这样的数据结构会使得运算效率提高的原因。
    当多个 CPU 都需要访问相同内存时,那么同一段内存的内容将会出现在多个 CPU 的cache 之中,这带来的一个问题就是,如何维护好它们之间的统一性就成了问题,比如,当同一块地址被两个 cache 同时缓存的时候,其中一个要改写,那么必须要存在一种手段去通知另外的 cache 即时更新,以免另一个 CPU 需要取数据时能得到最新的数据。
    缓存既然带来了这些问题,那么可不可以指令某些内存被访问时要不要被缓存呢,或者更准确地讲,当访问一块内存时如何控制缓存的形为呢?答案是可以的,我们可以指定内存的类型。通过 MTRRs 寄存器可以指定物理地址范围的内存类型,一般内存有这些常用类型:
    1. UC (Uncacheable), 表示这段内存不能被缓存。
    2. WT (Write Through), 表示写入时,cache 和内存都是更新。
    3. WB (Write Back), 表示只更新 cache, 合适时机再写回到内存中。
    4. WC (Write combining) 这种内存不会被 cache, 对于写操作可能会被延迟写入。
    但是往往,为了取得比较高的效率,普通内存的使用一般会选择 WB 类型,也就是内存会被 cache ,此时就需要一种手段来保证各个 cache 之间的一致性了,专业一点来讲就是 Cache-coherence 。
    那么如何知道自己 cache 中的 cache line 在其它 CPU 的 cache 中存在呢,如何记录自己的 cache line 被自己修改过,需要回写呢,显然要有一些状态标记来记录这些
东西,这就是 MESI 协议。
    在MESI协议中,每个Cache line有4个状态,可用2个bit表示,它们分别是: 
    1. M(Modified)
    这行数据有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。
    2. E(Exclusive)
    这行数据有效,数据和内存中的数据一致,数据只存在于本Cache中。
    3. S(Shared)
    这行数据有效,数据和内存中的数据一致,数据可能存在于很多Cache中。
    4. I(Invalid)
    这行数据无效。
    真相正在一步一步解开...

走进 CPU 的 Cache