入门
本节主要介绍了 CPU 多级缓存、CPU 指令重排序、内存屏障。
其中 多级缓存、指令重排序 都是 CPU 为了执行效率而做的优化。
内存屏障是为了解决多线程中指令重排序所带来的的问题。
CPU性能优化手段
缓存
作用
- 尽可能的降低 CPU 访问主内存带来的开销
多级缓存
分类
- L1 Cache(一级缓存)
- 是CPU第一层高速缓存,分为数据缓存和指令缓存。
- 一般服务器CPU的L1 缓存的容量通常在32 - 4096KB。
- L2
- 由于L1级高速缓存容量的限制,为了再次提高CPU的运算速度,在CPU外部放置一高速存储器,即二级缓存。
- L3
- L3缓存的应用可以进一步降低内存延迟,提升大数据量计算时处理器的性能。
- 具有较大L3缓存的处理器提供更有效的文件系统缓存行为及较短消息和处理器队列长度。
- 一般是多核共享一个L3缓存
读取顺序
L1 > L2 > L3 > 内存 > 硬盘
缓存同步协议MESI
解决的问题
- 多线程对统一数据读写时,写入主存的数据以谁为准的问题
状态分类
MESI协议,定义了缓存中,缓存状态位的状态(是否修改/是否存在于多个CPU)
- 修改态(Modified) :缓存内数据已被修改
-
- 此cache行已被修改过(脏行) ,内容已不同于主存,为此cache专有
- 专有态(Exclusive):只存在于某一CPU缓存且未被修改
-
- 此cache行内容同于主存,但不出现于其它cache中
- 共享态(Shared):被多个CPU缓存读取
-
- 此cache行内容同于主存,但也出现于其它cache中
- 无效态(Invalid):无效
-
- 此cache行内容无效(空行)
如何保证一致性
- 当单个CPU对缓存中数据进行修改时,要通知其他CPU
- 也就是说,CPU要监听其他CPU发来的缓存修改通知,从而保证最终一致性
指令重排
为了更好的程序执行效率
发生重排的场景
- 当CPU需要写缓存时,发现缓存区被其他CPU占用,则可以先执行后面的读缓存指令(跟异步IO的处理方式类似)
as-if-serial语义
- 无论如何重排序,都不能改变单线程下,程序原有的执行结果。
- 换句话说,编译器和CPU,不会对存在数据依赖关系的操作进行重排序
内存屏障
多级缓存/重排序可能存在的问题
多线程中的指令重排序问题
- as-if-serial 语义只保证了单线程下的数据依赖关系
- 在多线程下,无法判断不同CPU之间的逻辑关系,所以可能会导致因为乱序执行,而导致结果错误
CPU缓存可能存在的问题
缓存中的数据与主内存的数据并不是实时同步的,各CPU (或CPU核心) 间缓存的数据也不是实时同步。在同一个时间点,各CPU所看到同一内存地址的数据的值可能是不一致的。
如何解决
写内存屏障
- CPU 会在写指令后,插入 Store Barrier,能让写入 CPU缓存 中的数据立即写入主存
- 强制写入主存,CPU 就不会因为性能问题,而考虑指令重排
读内存屏障
- CPU 会在读指令之前,插入 Load Barrier,会强制使缓存数据失效,读取主存中最新的数据
- 强制读取主存,避免了缓存与主存的数据一致性问题
-
- PS:MESI 缓存同步协议,只保证了最终一致性