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并标记为【已修改】状态;


Cache并没有起到缓存的效果,虽然变量A、B没有任何关系,但是由于归属于同一个Cache line,这个Cache line中的任意数据被修改后,都会互相影响,从而重复最后两个步骤;