Cache与内存

程序局部性原理

        CPU 大多数时间在执行相同的指令或者与此相邻的指令。这就是大名鼎鼎的程序局部性原理

内存

        内存应该叫 DRAM,即动态随机存储器。内存储存颗粒芯片中的存储单元是由电容和相关元件做成的,电容存储电荷的多、少代表数字信号 0 和 1。

        控制内存刷新和内存读写的是内存控制器,而内存控制器集成在北桥芯片中。传统方式下,北桥芯片存在于系统主板上,而现在由于芯片制造工艺的升级,芯片集成度越来越高,所以北桥芯片被就集成到 CPU 芯片中了,同时这也大大提升了 CPU 访问内存的性能。而作为软件开发人员,从逻辑上我们只需要把内存看成一个巨大的字节数组就可以,而内存地址就是这个数组的下标。

Cache与内存_第1张图片

CPU 到内存的性能瓶颈        

        CPU 要数据,内存一时给不了怎么办?CPU 就得等,通常 CPU 会让总线插入等待时钟周期,直到内存准备好,到这里你就会发现,无论 CPU 的性能多高都没用,而内存才是决定系统整体性能的关键。

Cache

        CPU大多数时间都在访问相同或者与此相邻的地址,那么,如果我们用一块小而快的储存器,放在 CPU 和内存之间,就可以利用程序的局部性原理来缓解 CPU 和内存之间的性能瓶颈。这块小而快的储存器就是 Cache,即高速缓存。

        Cache 中存放了内存中的一部分数据,CPU 在访问内存时要先访问 Cache,若 Cache 中有需要的数据就直接从 Cache 中取出,若没有则需要从内存中读取数据,并同时把这块数据放入 Cache 中。但是由于程序的局部性原理,在一段时间内,CPU 总是能从 Cache 中读取到自己想要的数据。

        Cache 可以集成在 CPU 内部,也可以做成独立的芯片放在总线上,现在 x86 CPU 和 ARM CPU 都是集成在 CPU 内部的。其逻辑结构如下图所示:

Cache与内存_第2张图片

        Cache 主要由高速的静态储存器、地址转换模块和 Cache 行替换模块组成。

        Cache 会把自己的高速静态储存器和内存分成大小相同的行,一行大小通常为 32 字节或者 64 字节。Cache 和内存交换数据的最小单位是一行,为方便管理,在 Cache 内部的高速储存器中,多个行又会形成一组。

        除了正常的数据空间外,Cache 行中还有一些标志位,如脏位、回写位,访问位等,这些位会被 Cache 的替换模块所使用。

        Cache 大致的逻辑工作流程如下。

        1.CPU 发出的地址由 Cache 的地址转换模块分成 3 段:组号,行号,行内偏移。

        2.Cache 会根据组号、行号查找高速静态储存器中对应的行。如果找到即命中,用行内偏移读取并返回数据给 CPU,否则就分配一个新行并访问内存,把内存中对应的数据加载到 Cache 行并返回给 CPU。写入操作则比较直接,分为回写和直通写,回写是写入对应的 Cache 行就结束了,直通写则是在写入 Cache 行的同时写入内存。

        3. 如果没有新行了,就要进入行替换逻辑,即找出一个 Cache 行写回内存,腾出空间,替换行有相关的算法,替换算法是为了让替换的代价最小化。例如,找出一个没有修改的 Cache 行,这样就不用把它其中的数据回写到内存中了,还有找出存在时间最久远的那个 Cache 行,因为它大概率不会再访问了。以上这些逻辑都由 Cache 硬件独立实现,软件不用做任何工作,对软件是透明的。

Cache 数据一致性问题

        Cache 虽然带来性能方面的提升,但同时也给和硬件和软件开发带来了问题,那就是数据一致性问题。

Cache与内存_第3张图片

        上图是一颗最简单的双核心 CPU,有三级 Cache,第一级 Cache 是指令和数据分开的,第二级 Cache 是独立于 CPU 核心的,第三级 Cache 是所有 CPU 核心共享的。

        Cache 的一致性问题,主要包括这三个方面:

        1. 一个 CPU 核心中的指令 Cache 和数据 Cache 的一致性问题。

        2. 多个 CPU 核心各自的 2 级 Cache 的一致性问题。

        3.CPU 的 3 级 Cache 与设备内存,如 DMA、网卡帧储存,显存之间的一致性问题。

        为了解决这些问题,硬件工程师们开发了多种协议,典型的多核心 Cache 数据同步协议有 MESI 和 MOESI。MOESI 和 MESI 大同小异。

Cache 的 MESI 协议

        MESI 协议定义了 4 种基本状态:M、E、S、I,即修改(Modified)、独占(Exclusive)、共享(Shared)和无效(Invalid)

        1.M 修改(Modified):当前 Cache 的内容有效,数据已经被修改而且与内存中的数据不一致,数据只在当前 Cache 里存在。比如说,内存里面 X=5,而 CPU 核心 1 的 Cache 中 X=2,Cache 与内存不一致,CPU 核心 2 中没有 X

Cache与内存_第4张图片

        2.E 独占(Exclusive):当前 Cache 中的内容有效,数据与内存中的数据一致,数据只在当前 Cache 里存在;类似 RAM 里面 X=5,同样 CPU 核心 1 的 Cache 中 X=5(Cache 和内存中的数据一致),CPU 核心 2 中没有 X

Cache与内存_第5张图片

        3.S 共享(Shared):当前 Cache 中的内容有效,Cache 中的数据与内存中的数据一致,数据在多个 CPU 核心中的 Cache 里面存在。例如在 CPU 核心 1、CPU 核心 2 里面 Cache 中的 X=5,而内存中也是 X=5 保持一致。

Cache与内存_第6张图片

        4.无效(Invalid):当前 Cache 无效。前面三幅图 Cache 中没有数据的那些,都属于这个情况

开启 Cache

        前面目前 Cache 已经成为了现代计算机的标配,但是 x86 CPU 上默认是关闭 Cache 的,需要在 CPU 初始化时将其开启。在 x86 CPU 上开启 Cache 非常简单,只需要将 CR0 寄存器中 CD、NW 位同时清 0 即可。CD=1 时表示 Cache 关闭,NW=1 时 CPU 不维护内存数据一致性。所以 CD=0、NW=0 的组合才是开启 Cache 的正确方法

        开启 Cache 只需要用四行汇编代码,代码如下:
        
        mov eax, cr0
        ;开启 CACHE    
        btr eax,29 ;CR0.NW=0
        btr eax,30  ;CR0.CD=0
        mov cr0, eax
        

你可能感兴趣的:(c语言,开发语言)