离处理器越近,访问速度就越快,造价也就越高,同时容量也会更小。缓存是处理器和内存之间的一个桥梁,通常分为多层,包括 L1 层、L2 层、L3 层等等。缓存的速度介于处理器和内存之间,访问处理器内部寄存器的速度在 1ns 以内(一个时钟周期),访问内存的速度通常在 50~100ns(上百个时钟周期)之间。那么对于缓存来讲,靠近处理器最近的 L1 层缓存的访问速度在 1ns~2ns(3 个时钟周期)左右,外层 L2 和 L3 层的访问速度在 10ns~20ns(几十个时钟周期)之间。
在多核芯片上,缓存集成的方式主要有以下三种:
- 集中式缓存:一个缓存和所有处理器直接相连,多个核共享这一个缓存;
- 分布式缓存:一个处理器仅和一个缓存相连,一个处理器对应一个缓存;
- 混合式缓存:在 L3 采用集中式缓存,在 L1 和 L2 采用分布式缓存。
缓存块替换策略需要达到的一个目标是:被替换出的数据块应该是将来最晚会被访问的块。然而,对将来即将发生的事情是没有办法预测的,因为处理器并不知道程序将来会访问哪个地址。因此,现在的缓存替换策略都采用了最近最少使用算法(Least Recently Used ,LRU)或者是类似 LRU 的算法。
引起缓存缺失的类型主要有三种:
- 强制缺失:第一次将数据块读入到缓存所产生的缺失,也被称为冷缺失(cold miss),因为当发生缓存缺失时,缓存是空的(冷的);
- 冲突缺失:由于缓存的相连度有限导致的缺失;
- 容量缺失:由于缓存大小有限导致的缺失。
伪共享(false-sharing)的意思是说,当两个线程同时各自修改两个相邻的变量,由于缓存是按缓存块来组织的,当一个线程对一个缓存块执行写操作时,必须使其他线程含有对应数据的缓存块无效。这样两个线程都会同时使对方的缓存块无效,导致性能下降。
解决伪共享的办法是,做数据填充。在 Java 的并发库里经常会看到为了解决伪共享而进行的数据填充。这是大家在写并发程序时也要加以注意的。
此文章为7月Day14学习笔记,内容来源于极客时间《编程高手必学的内存知识》