1. CPU缓存:为何不可或缺?
随着时间的推移,摩尔定律揭示了CPU和内存之间性能差距的加剧。为了缩小这一差距,CPU缓存应运而生。
CPU缓存的功能
-
性能差异的弥补者:将内存中频繁访问的数据和指令加载到L1-L3缓存中,减少CPU等待内存响应的时间。
-
高效的数据存取:CPU缓存以Cache Line为单位读取数据,通常是64字节,这优化了数据的读取过程。
2. CPU缓存的读取机制
CPU在访问数据时,总是优先检查其高速缓存。只有当CPU在Cache中找不到数据的时候,才会去访问内存,并将读取到的数据写入Cache之中。
直接映射缓存
-
地址映射:内存地址被分为索引、组标记和偏移量,利用这种结构将内存地址映射到CPU缓存地址。
-
读取过程:
- 使用内存地址的低位计算缓存中的索引。
- 检查缓存数据的有效性。
- 通过组标记确认所需数据。
- 使用偏移量从数据块中读取所需数据。
3. 缓存写入策略
处理缓存与主内存之间的数据一致性问题
两种主要策略:
-
写直达(Write-Through):数据同时更新到缓存和主内存。简单且保证数据一致性,减少数据丢失风险,但有性能开销。
-
写回(Write-Back):仅更新缓存,需要时才同步到主内存。
多核CPU中的缓存一致性
随着技术的进步,单核CPU的主频提升遇到了物理和技术上的限制。为了继续提高处理能力,出现了多核CPU,然而,这种设计带来了一个新的问题:缓存一致性。
缓存一致性问题
- 场景描述:以更新iPhone价格为例,假设我们采用写回策略,在1号核心更新价格并写入其L2缓存时,这个缓存块被标记为“脏”(即已修改但未同步)。这意味着数据尚未同步到L3缓存或主内存。此时,1号核心和2号核心的缓存中关于iPhone价格的信息可能是不一致的。
缓存一致性的解决需求
为了解决这一问题,需要实现以下两个机制:
- 写传播:一个核心的缓存数据更新必须能够及时传播到其他核心对应的缓存行中。
- 事务的串行化:保证一个核心内部的读取和写入操作对其他核心是按相同顺序可见的。
缓存一致性的解决机制
-
总线嗅探(Bus Snooping):
- 定义:通过它,所有的缓存操作请求都通过总线广播给所有CPU核心,让各核心根据自己的缓存情况进行相应的处理。
-
MESI协议:
- 概述:MESI协议是基于写失效机制的缓存一致性协议,包括Modified(已修改)、Exclusive(独占)、Shared(共享)和Invalid(失效)四种状态。
- 优势:减少了总线上的数据传输需求,仅需要传输操作信号和地址信号,节约了总线带宽。
-
写策略:
- 写直达(Write-Through):数据同时写入缓存和主内存,保持数据的一致性,但可能影响性能。
- 写回(Write-Back):数据首先仅写入缓存,稍后再同步到主内存。这种方法可以提高性能,但需要额外的机制来保持一致性。
-
锁和同步机制:
- 在多线程和多核心环境中,使用锁或者原子操作来确保对共享资源的访问是互斥的,从而保持数据一致性。
- Java程序中的volatile关键字:在Java多线程编程中,
volatile
关键字保证变量的读写直接对主内存进行,确保所有线程都能看到共享变量的最新状态。
-
一致性内存模型:
- 现代多核处理器和编程语言提供了内存模型,如Java内存模型(JMM),定义了线程如何通过内存进行交互。