CPU架构
介绍
CPU包括:寄存器、CPUCache、运算器、控制器;
- 一个CPU里通常会有多个CPU核心,比如图上的核心1、核心2。
- 每个CPU核心都有自己的L1 Cache和L2 Cache;
- L1 Cache通常分为dCache(数据缓存)和iCache(指令缓存);
- L3 Cache是多个核心共享的;
目的
Cache作为CPU与内存之间的缓存层,以减少对内存访问频率;
程序的局部性
CPU从内存中读取数据到Cache的时候,并不是一个字节一个字节的读取,而是一块一块的方式来读取数据的,这一块一块的数据被称为CPU line(缓存行),所以CPU line是CPU从内存读取数据到Cache的单位;
思维脑图
实现数据的一致性
CPU Cache与内存之间如何实现一致性
写直达:数据同时写入内存和Cache中
情况:
- 如果数据已经在Cache中,先将数据更新到Cache里面,再写入到内存里面;
- 如果数据没有在Cache中,就直接把数据更新到内存里面。
问题:
- 无论数据在不在Cache里面,每次写操作都会写回到内存,会造成花费大量的时间,性能会受到很大的影响。
写回:
- 新的数据仅仅写入Cache Block中并标记为脏的;
- 脏的含义:表示Cache与内存中的数据不一致,需要将cache已有的数据先写入到内存中,之后再将新数据写入cache。
情况:
- 如果缓存命中,则将数据更新到Cache里面,并标记这条数据是脏的;
- 如果缓存未命中,一种情况:缓存没有数据或者存放其他数据并且不是脏的(说明缓存与内存数据一致可以直接覆盖),则直接将数据写入到缓存中并标记为脏的;另一种情况:缓存里存放其他数据并且是脏的,则需要先将缓存中的数据写入到内存中,再将新的数据写入cache并标记为脏的。
多核CPU缓存一致性的问题
MESI协议
- Modified(已修改):表示Cache block上的数据已经被更新过了,但是还没有写到内存中;
- Exclusive(独占):表示数据只存在于一个CPU核心的Cache里,而其他CPU核心的Cache没有该数据。可以直接自由的写入,不需要通知其他CPU核心;
- Shared(共享):表示数据存在于多个CPU核心的Cache里,不能直接修改,需要先将其他核心的Cache中对应的Cache line标记为【无效】状态,然后再更新当前的Cache里面的数据;
- invalidated(已失效):表示Cache block上的数据已经失效了,不可以读取该状态的数据。需要从内存中读取到Cache line;
伪共享
介绍
多个线程同时读写同一个Cache line的不同变量时,会造成CPU Cache失效的现象称为伪共享;
解决
通过字符填充;
一个cache line包含62个kb字节,所以使用java的7个long类型+对象头8字节即可实现填充
举例
- CPU核心1绑定A线程只读写变量A、CPU核心2绑定B线程只读写变量B;
- 核心1读取变量A,由于CPU从内存读取数据到Cache的单位是Cache line,同时变量A、B同时属于同一个Cache line;
- 所以A和B都会被加载到Cache,并将此Cache line标记为【独占】状态;
核心2读取变量B,变量A、B被加载到Cache,并将核心1、核心2的Cache line状态变为【共享】状态;
- 核心1需要修改变量A,发现此Cache line的状态是【共享】状态,所以先需要通过总线发送消息给核心2,
- 通知核心2把Cache中对应的Cache line标记为【已失效】状态,然后核心1对应的Cache line状态变为【已修改】状态,并且修改变量A
- 核心修改变量B,此时核心2的Cache中对应的Cache line是已失效的状态,另外由于核心1的Cache也有此相同的数据,且状态为【已修改】状态;
- 所以要先将核心1的Cache对应的Cache line写回内存,然后核心2再从内存读取Cache line大小的数据到Cache中,最后把变量B修改到核心2的Cache并标记为【已修改】状态;