对于没有接触过底层技术的朋友来说,或许从未听说过cache。毕竟cache的存在对程序员来说是透明的。在接触cache之前,先为你准备段code分析。
int arr[10][128];
for (i = 0; i < 10; i++)
for (j = 0; j < 128; j++)
arr[i][j] = 1;
如果你曾经学习过C/C++语言,这段code自然不会陌生。如此简单的将arr
数组所有元素置1。 你有没有想过这段code还有下面的一种写法。
int arr[10][128];
for (i = 0; i < 128; i++)
for (j = 0; j < 10; j++)
arr[j][i] = 1;
功能完全一样,但是我们一直在重复着第一种写法(或许很多的书中也是建议这么编码),你是否想过这其中的缘由?文章的主角是cache,所以你一定猜到了答案。那么cache是如何影响这2段code的呢?
为什么需要cache
在思考为什么需要cache之前,我们首先先来思考另一个问题:我们的程序是如何运行起来的?
我们应该知道程序是运行在 RAM之中,RAM 就是我们常说的DDR(例如: DDR3、DDR4等)。我们称之为main memory(主存)。当我们需要运行一个进程的时候,首先会从磁盘设备(例如,eMMC、UFS、SSD等)中将可执行程序load到主存中,然后开始执行。在CPU内部存在一堆的通用寄存器(register)。如果CPU需要将一个变量(假设地址是A)加1,一般分为以下3个步骤:
- CPU 从主存中读取地址A的数据到内部通用寄存器 x0(ARM64架构的通用寄存器之一)。
- 通用寄存器 x0 加1。
- CPU 将通用寄存器 x0 的值写入主存。
我们将这个过程可以表示如下:
其实现实中,CPU通用寄存器的速度和主存之间存在着太大的差异。两者之间的速度大致如下关系:
CPU register的速度一般小于1ns,主存的速度一般是65ns左右。速度差异近百倍。因此,上面举例的3个步骤中,步骤1和步骤3实际上速度很慢。当CPU试图从主存中load/store 操作时,由于主存的速度限制,CPU不得不等待这漫长的65ns时间。如果我们可以提升主存的速度,那么系统将会获得很大的性能提升。如今的DDR存储设备,动不动就是几个GB,容量很大。如果我们采用更快材料制作更快速度的主存,并且拥有几乎差不多的容量。其成本将会大幅度上升。我们试图提升主存的速度和容量,又期望其成本很低,这就有点难为人了。因此,我们有一种折中的方法,那就是制作一块速度极快但是容量极小的存储设备。那么其成本也不会太高。这块存储设备我们称之为cache memory。在硬件上,我们将cache放置在CPU和主存之间,作为主存数据的缓存。 当CPU试图从主存中load/store数据的时候, CPU会首先从cache中查找对应地址的数据是否缓存在cache 中。如果其数据缓存在cache中,直接从cache中拿到数据并返回给CPU。当存在cache的时候,以上程序如何运行的例子的流程将会变成如下:
CPU和主存之间直接数据传输的方式转变成CPU和cache之间直接数据传输。cache负责和主存之间数据传输。
多级cache存储结构
cahe的速度在一定程度上同样影响着系统的性能。一般情况cache的速度可以达到1ns,几乎可以和CPU寄存器速度媲美。但是,这就满足人们对性能的追求了吗?并没有。当cache中没有缓存我们想要的数据的时候,依然需要漫长的等待从主存中load数据。为了进一步提升性能,引入多级cache。前面提到的cache,称之为L1 cache(第一级cache