Cache与内存:程序放在哪儿?

首先问个问题:程序放到哪儿?也就是换句话指令和数据放在哪儿?

是放到内存中吗?内存是什么?用软件的说法它是个栈一个数组,但是最终还是要到硬件上面,内存对应的应该是物理地址下的内存空间,我们用的内存怎么取选取的,我们用了哪些内存?我们只是通过内存来存取数据吗?如果不是简单的从内存中存取数据,那还需要什么?

内存存取数据是很慢的,这个慢是相对于CPU而言的,CPU是进程管理单元,进程管理就是指令和数据的存取,CPU的速度是很快的,那么这是内存就相对而言是慢的,CPU想要的数据取不到,CPU像存的数据存不下,这时候就是所谓的瓶颈,怎么办?

Cache出现了,又名高速缓存,就嵌入CPU中的,服务于CPU,主要用作CPU和内存之间,用于配合CPU的速度。。。

那么问题来了,寄存器,cache,内存,这三个构成了计算机指令和数据的存取,那么顺序寄存器《-》cache《-》内存,其实寄存器和cache的存在意义实际上是提高时间效率,而上一节中的虚拟地址-》物理地址(磁盘)其意义在于提高空间的利用率。

那么问题又来了,cache是如何工作的?如何提高数据存取效率?它是由什么组成的,为何那么神奇?如果多进程时cache中的数据能否共享最新的数据?它是完美的吗?如果不是有何缺陷?

@极客时间彭东大神的原文基本先复制一遍(好东西。。。)

从一段经典代码看局部性原理

不知道,你还记不记得 C 语言打印九九乘法表的代码,想不起来也没关系,下面我把它贴出来,代码很短,也很简单,就算你自己写一个也用不了一分钟,如下所示

#include <stdio.h>

int main(){

    int i,j;

    for(i=1;i<=9;i++){       

        for(j=1;j<=i;j++){

            printf("%d*%d=%2d  ",i,j,i*j);

        }

        printf("\n");

    }

    return 0;

}

我们当然不是为了研究代码本身,这个代码非常简单,这里我们主要是观察这个结构,代码的结构主要是顺序、分支、循环,这三种结构可以写出现存所有算法的程序。

我们常规情况下写的代码是顺序和循环结构居多。上面的代码中有两重循环,内层循环的次数受到外层循环变量的影响。

就是这么简单,但是越简单的东西越容易看到本质。

可以看到,这个代码大数时间在执行一个乘法计算和调用一个 printf 函数,而程序一旦编译装载进内存中,它的地址就确定了。也就是说,CPU 大多数时间在访问相同或者与此相邻的地址,换句话说就是:CPU 大多数时间在执行相同的指令或者与此相邻的指令。这就是大名鼎鼎的程序局部性原理。(比方说for循环)

内存

明白了程序的局部性原理之后,我们再来看看内存。

你或许感觉这跨越有点大,但是只有明白了内存的结构和特性,你才能明白程序局部性原理的应用场景和它的重要性。

内存也可称为主存,不管硬盘多大、里面存放了多少程序和数据,只要程序运行或者数据要进行计算处理,就必须先将它们装入内存。

我们先来看看内存长什么样(你也可以上网自行搜索),如下图所示。

 内存条

从上图可以看到在 PCB 板上有内存颗粒芯片,主要是用来存放数据的。SPD 芯片用于存放内存自身的容量、频率、厂商等信息。还有最显眼的金手指,用于连接数据总线和地址总线,电源等。

其实从专业角度讲,内存应该叫 DRAMDynamic random-access memory),即动态随机存储器。内存储存颗粒芯片中的存储单元是由电容和相关元件做成的,电容存储电荷的多、少代表数字信号 0 1

而随着时间的流逝,电容存在漏电现象,这导致电荷不足,就会让存储单元的数据出错,所以 DRAM 需要周期性刷新,以保持电荷状态。

DRAM 结构较简单且集成度很高,通常用于制造内存条中的储存颗粒芯片。虽然内存技术标准不断更新,但是储存颗粒的内部结构没有本质改变,还是电容存放电荷,标准看似更多,实际上只是提升了位宽、工作频率,以及传输时预取的数据位数。(感觉想到了模电转数电。。。)

比如 DDR SDRAM,即双倍速率同步动态随机存储器,它使用 2.5V 的工作电压,数据位宽为 64 位,核心频率最高为 166MHz。下面简称 DDR 内存,它表示每一个时钟脉冲传输两次数据,分别在时钟脉冲的上升沿和下降沿各传输一次数据,因此称为双倍速率的 SDRAM。后来的 DDR2DDR3DDR4 也都在核心频率和预取位数上做了提升。

最新的 DDR4 采用 1.2V 工作电压,数据位宽为 64 位,预取 16 位数据。DDR4 取消了双通道机制,一条内存即为一条通道,工作频率最高可达 4266MHz,单根 DDR4 内存的数据传输带宽最高为 34GB/s。其实我们无需过多关注内存硬件层面的技术规格标准,重点需要关注的是,内存的速度还有逻辑上内存和系统的连接方式和结构,这样你就能意识到内存有多慢,还有是什么原因导致内存慢的。

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

那么内存是为何慢了?

首先CPU 读取内存要通过北桥芯片去读,并不是直接取,第二就是内存本身的带宽和CPU 也差好几个数量级,所以慢。

CPU 到内存的性能瓶颈

尽管 CPU 和内存是同时代发展的,但 CPU 所使用技术工艺的材料和内存是不同的,侧重点也不同,价格也不同。如果内存使用 CPU 的工艺和材料制造,那内存条的昂贵程度会超乎想象,没有多少人能买得起。

由于这些不同,导致了 CPU 和内存条的数据吞吐量天差地别。尽管最新的 DDR4 内存条带宽高达 34GB/s,然而这相比 CPU 的数据吞吐量要慢上几个数量级。再加上多核心 CPU 同时访问内存,会导致总线争用问题,数据吞吐量会进一步下降。

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 主要由高速的静态储存器、地址转换模块和 Cache 行替换模块组成。

Cache 会把自己的高速静态储存器和内存分成大小相同的行,一行大小通常为 32 字节或者 64 字节。

Cache 和内存交换数据的最小单位是一行,为方便管理,在 Cache 内部的高速储存器中,多个行又会形成一组。除了正常的数据空间外,Cache 行中还有一些标志位,如脏位、回写位,访问位等,这些位会被 Cache 的替换模块所使用。

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

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

2.Cache 会根据组号、行号查找高速静态储存器中对应的行。如果找到即命中,用行内偏移读取并返回数据给 CPU,否则就分配一个新行并访问内存,把内存中对应的数据加载到 Cache 行并返回给 CPU。写入操作则比较直接,分为回写和直通写,回写是写入对应的 Cache 行就结束了,直通写则是在写入 Cache 行的同时写入内存。(我的理解:2步骤应该是找数据。读数据,如果cacheCPU想要的数据就直接读,如果没有,CPU从内存中读,然后也把数据存到cache中方便下一次读取;写数据,回写,是cpu只往cache中;写数据,原来的地址还在,数据啥的没啥变化,原来的数据(后面会有MESI中修改状态就是和这差不多),直通写,就是写入cache也写入内存,也可能是指令,或是地址,或是数据变化了。。。)

3.如果没有新行了,就要进入行替换逻辑,即找出一个 Cache 行写回内存,腾出空间,替换行有相关的算法,替换算法是为了让替换的代价最小化。例如,找出一个没有修改的 Cache 行,这样就不用把它其中的数据回写到内存中了,还有找出存在时间最久远的那个 Cache 行,因为它大概率不会再访问了。以上这些逻辑都由 Cache 硬件独立实现,软件不用做任何工作,对软件是透明的。(我的理解:应该是找指令,指令是新的,那就加一条,如果cache未满,直接加行,如果cache满了,直接删行,那么问题来了,删除哪一行呢?算法说,删好长时间用不到的,也就是cpu好长时间没去存取数据的,另一种就是cpu去访问了,但是里面的数据始终没啥变化的,这两种的行要删除,那么第二中为啥要删,万一还有用呢?如果还用的上就直接去内存中,内存中有)(我理解的cache2中数据应该是新的常用的,时常变化的数据,地址还在数据变了,就像99乘法表中的两个变量,变量名没变,地址没变,值一直在变)。

以上这些逻辑都由 Cache 硬件独立实现,软件不用做任何工作,对软件是透明的。

(透明就是硬件设计,软件不需要去设计)

Cache 带来的问题

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

为了搞清楚这个问题,我们必须先搞清楚 Cache 在硬件层面的结构,下面我画了 x86 CPU Cache 结构图:

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

下面来看看 Cache 的一致性问题,主要包括这三个方面.

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

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

3.CPU 3 Cache 与设备内存,如 DMA、网卡帧储存,显存之间的一致性问题。这里我们不需要关注这个问题。

我们先来看看 CPU 核心中的指令 Cache 和数据 Cache 的一致性问题,对于程序代码运行而言,指令都是经过指令 Cache,而指令中涉及到的数据则会经过数据 Cache。所以,对自修改的代码(即修改运行中代码指令数据,变成新的程序)而言,比如我们修改了内存地址 A 这个位置的代码(典型的情况是 Java 运行时编译器),这个时候我们是通过储存的方式去写的地址 A,所以新的指令会进入数据 Cache。但是我们接下来去执行地址 A 处的指令的时候,指令 Cache 里面可能命中的是修改之前的指令。所以,这个时候软件需要把数据 Cache 中的数据写入到内存中,然后让指令 Cache 无效,重新加载内存中的数据。

(这段话的意思就是,一条指令里面是带数据的,mov edx 0x9876,瞎写一个,如果我们修改了这个地址的数据我把这个地址里面的变量+1了,那么这是cache中的数据还是之前的没加1的数据,我们需要把这个加1的数据先写如内存,然后再由内存写入cache,)理解 有点像定时器的样子,如果指令中的地址存放的数据每次在变的时候,需要不断的刷新地址中数据,怎么刷新?需要先把数据-》内存-cache

再来看看多个 CPU 核心各自的 2 Cache 的一致性问题。从上图中可以发现,两个 CPU 核心共享了一个 3 Cache。比如第一个 CPU 核心读取了一个 A 地址处的变量,第二个 CPU 也读取 A 地址处的变量,那么第二个 CPU 核心是不是需要从内存里面经过第 321 Cache 再读一遍,这个显然是没有必要的。

在硬件上 Cache 相关的控制单元,可以把第一个 CPU 核心的 A 地址处 Cache 内容直接复制到第二个 CPU 的第 21 Cache,这样两个 CPU 核心都得到了 A 地址的数据。不过如果这时第一个 CPU 核心改写了 A 地址处的数据,而第二个 CPU 核心的 2 Cache 里面还是原来的值,数据显然就不一致了。

为了解决这些问题,硬件工程师们开发了多种协议,典型的多核心 Cache 数据同步协议有 MESI MOESIMOESI MESI 大同小异,下面我们就去研究一下 MESI 协议。

(这段话意思是,例如两个进程用同一个数据怎么办?数据如何实时共享?3cache是干这个滴,用MESI协议,另外一个反向思维,2cache是独立的,独立意味着数据的私有;这样就通了,每个cache可以把私有数据公有,也可以把公有数据私有)

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

感谢@极客时间彭东大神的课,图片基本都是使用课上的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值