本文已参与「新人创作礼」活动,一起开启掘金创作之路。
CPU Cache
现代CPU和内存访问速度差距非常大,如果CPU直接访问内存的话,CPU就要等很长时间。为了弥补两者之间的性能差异,我们引入了CPU Cache。
我们把CPU Cache放到CPU中,内存中的指令、数据就会被加载到L1~L3 Cache。在95%的情况下,CPU只需要访问Cache就可以从中读取到指令和数据,而不需要访问内存。
CPU Cache是如何读取数据的
CPU进行数据读取的时候,不论数据是否在Cache中,都会先访问Cache。只有在Cache中找不到数据的时候,才会访问内存,并将读取到数据写入Cache中。
CPU知道自己要读取内存中某个位置的数据,那么它如何才能知道要访问的内存数据,存储在Cache的哪个位置?这里我们引入直接映射 Cache的方法。
Cache的索引地址
CPU访问内存数据,首先拿到的就是数据所在内存块的地址。而直接映射 Cache的方法,就是确保每一个内存块地址,始终映射到一个固定的CPU Cache地址(Cache Line,大小通常是64字节)。这个映射关系我们呢通常用求余运算来实现。
而实际中,当我们把高速缓存块分为2的N次方个,那么求余的结果,就是内存块地址的低N位。
现在,问题又来了,多个内存块的地址映射到同一个缓存块,那我们如何来判断到底是要访问哪一个内存块的数据?
Cache的组标记
内存块的低N位地址被用来索引,剩下的高位,它本身就可以用来判断不同的内存块地址,所以我们自然而然把它当作Cache的组标记来判断到底要访问众多内存块中的哪一个。
Cache的有效位
它是用来标记对应的Cache Line中的数据是否是有效的。如果有效位是0,CPU就直接去访问内存去了,并且重新加载数据。
Cache的数据
我们知道CPU访问内存的时候,不光有内存块地址,还紧跟着偏移量,就是要访问数据在内存块中的相对位置。这个偏移量,也用来确定一个缓存块映射的多个内存块中数据的位置。
至此我们已经把内存地址,完全映射到高速缓存中了,如果我们要访问的数据,已经在缓存中了,如果我们要再访问它,就会经历这四步:
- 根据内存地址的地位,计算在Cache中的索引
- 判断有效位,确认数据是有效的
- 对比内存访问地址的高位和Cache中的组标记,确认它就是我们要访问的数据,从缓存块中读取对应的数据块
- 根据内存地址的偏移量,从数据块中确定想要的东西
如果在2、3步出错,CPU就会转而去访问内存,并更新在缓存块中的对应的数据。
CPU Cache是如何写入数据的
对于写入数据,我们应该写到Cache里还是主内存,如果直接写入到主内存里,Cache里的数据是否会失效?
写直达
用这种方法,每一次数据都要写入到主内存里。写入前,我们会先去判断要更新的数据是否已经在Cache里。如果在,我们会先把数据写入跟新到Cache里,再写入到主内存里;如果不在就只更新主内存。
写回
如果我们发现要更新的数据,原来就在Cache里,我们就只更新Cache里面的数据。同时把它标记为脏——就是表示Cache中的数据和主内存中的不一致。
如果发现要更新的数据(目标数据),原来不在Cache里,我们就要先检查准备存放它位置的数据(原数据)是否是脏的,是脏的,就需要把原数据写回到主内存后,再存放我们的目标数据并把原数据覆盖掉,同时还要把目标数据标记成脏的。不是脏的,我们就可以直接存放目标数据并把原数据覆盖掉,同时标记目标数据为脏。
用写回策略的话,我们从内存中读取数据的时候也要看准备存放它位置的原数据是否为脏。是的话,也要先把原数据写入住内存后再放我们的的新数据。