CPU多级缓存

CPU多级缓存

左图为最简单的高速缓存的配置,数据的读取和存储都经过高速缓存,CPU核心与高速缓存有一条特殊的快速通道;主存和高速缓存都连在系统总线上,这条总线还用于其他组件的通信。
高速缓存出现不久,系统变得越来越复杂,高速缓存与主存之间的速度差异被拉大,直到加入了另一级缓存,新加入的这级缓存比第一缓存更大,而且更慢,而且经济上不合适,所以有了二级缓存,甚至是三级缓存。
CPU多级缓存_第1张图片

  1. 为什么需要CPU cache?

    CPU的频率太快了,快到主存跟不上,这样在处理器时钟周期内,CPU常常需要等待主存,浪费资源,所 以cache的出现,是为了缓解CPU和内存之间速度的不匹配问题(结构:cpu->cache->memort)

  2. CPU cache有什么意义?

    1、时间局部性:如果某个数据被访问,那么在不久的将来他很可能被再次访问
    2、空间局部性:如果某个数据被访问,那么与他相邻的数据很快也可能被访问

  3. 缓存一致性(MESI)
    CPU中每个缓存行使用四种状态进行标记
    M:Modified 修改
    指的是该缓存行只被缓存在该CPU缓存中,并且是被修改过的,因此他与主存的数据是不一致的,该缓存行中的数据需要在未来的某个时间点(允许其他CPU读取主存相应的内容之前)写回主存,然后状态变成E(独享)。

    E :Exclusive 独享
    缓存行只被缓存在该CPU的缓存中,是未被修改过的,与主存的数据是一致的,可以在任何时刻当有其他CPU读取该内存时,变成S(共享)状态,当CPU修改缓存行的内容时,变成M(修改)的状态。

    S :Share 共享
    意味着该缓存行可能被多个CPU进行缓存,并且该缓存中的数据与主存数据是一致的,当有一个CPU修改该缓存行时,其他CPU是可以被作废的,变成I(无效的)。

    I :Invalid 无效的

    代表这个缓存是无效的,可能是有其他CPU修改了该缓存行。

    对应的四个操作
    local read:读本地缓存
    local write:写本地缓存
    remote read:将内存中的数据读取过来
    remote write:将数据写回主存

    在一个典型的多核系统中,每一个核都会有自己的缓存来共享总线,每一个CPU会发出读写(I/O)请求,而缓存的目的是为了减少CPU读写共享主存的次数;
    一个缓存除了在无效(Invalid)状态,都可以满足CPU的读请求,一个无效(Invalid)的缓存行必须从主存中读取(变成Share或者Exclusive状态)来满足该CPU的读请求。
    一个写请求只有在该缓存行是修改(Modified)或者独享(Exclusive)状态时才能被执行,如果缓存行处于共享(Share)状态,必须先将其他缓存中该缓存行变成无效(Invalid)状态(也即是不允许不同CPU同时修改同一缓存行,即使修改该缓存行中的不同数据也不允许)。该操作经常作用广播的方式来完成,例如:Request For Ownership(RFO)。
    缓存可以随时将一个非修改(Modified)状态的缓存行作废,或者变成无效(Invalid)状态,而一个修改(Modified)状态的缓存行必须先被写回主存。
    一个处于共享(Share)状态的缓存行业必须监听其他缓存使该缓存行无效或者独享该缓存行的请求,并将改缓存行变成无效(Invalid)。
    一个处于独享(Exclusive )状态的缓存行也必须监听其他缓存读主存中该缓存行的操作,一旦有这种操作,该缓存行需要变成共享(Share)状态。
    对于修改(Modified)和独享(Exclusive)状态而言总是精确的,他们在和该缓存行的真正状态是一致的。而共享(Share)状态可能是非一致的,如果一个缓存将处于共享(Share)状态的缓存行作废了,而另一个缓存实际上可能已经独享了该缓存行,但是该缓存却不会将该缓存行升迁为独享(Exclusive)状态,这是因为其他缓存不会广播他们作废掉该缓存行的通知,同样由于缓存并没有保存该缓存行的copy的数量,因此也没有办法确定自己是否已经独享(Share了该缓存行)。
    从上面的意义看来独享(Exclusive)状态时一种投机性的优化:如果一个CPU想修改一个处于共享(Share)状态的缓存航,总线事务需要将所有缓存行的copy变成Invalid状态,而修改独享(Exclusive)状态的缓存不需要使用总线事务。
    多核缓存协同操作
    假设有三个CPU A、B、C,对应三个缓存分别是cache a、b、c。在主内存中定义了x的引用值为。
    CPU多级缓存_第2张图片
    单核读取
    那么执行流程是:
    CPU A发出一条指令,从主内存中读取x。
    从主内存通过bus读取到缓存中(远端读取Remote read),这是该Cache line修改为E状态。
    CPU多级缓存_第3张图片
    双核读取
    那么执行流程是:
    CPU A发出了一条指令,从主内存中读取x。
    CPU A从主内存通过bus读取到cache a中并将cache line设置为E状态。
    CPU B发出了一条指令,从主内存中读取x。
    CPU B试图从主内存中读取x时,CPU A检测到了地址冲突。这时CPU A对相关数据做出反应。此时X存储于cache a和cache b中,x在chache a和cache b中都被设置为S状态(共享)。
    CPU多级缓存_第4张图片
    修改数据
    那么执行流程是:
    CPU A计算完成后发指令需要修改x。
    CPU A将x设置为M状态(修改)并通知缓存了x的CPU B,CPUB B将本地cache b中的x设置为I状态。
    CPU A对x进行赋值。
    CPU多级缓存_第5张图片
    同步数据
    那么执行流程是:
    CPU B发出了要读取x的指令。
    CPU B通知CPU A,CPU A将修改后的数据同步到主内存时cache a修改为E(独享)。
    CPU A同步CPU B的x,将cache a和同步后cache b中的x设置为S状态(共享)。
    CPU多级缓存_第6张图片
    MESI优化和他们引入的问题
    缓存的一致性消息传递是要时间的,这就使其切换时会产生延迟。当一个缓存被切换状态时其他缓存收到消息完成各自的切换并且发出回应消息这么一长串的时间中CPU都会等待所有缓存响应完成。可能出现的阻塞都会导致各种各样的性能问题和稳定性问题。
    CPU切换状态阻塞解决-存储缓存(Store Bufferes)
    比如你需要修改本地缓存中的一条信息,那么你必须将I状态通知到其他拥有该缓存数据的CPU缓存中,并且等待确认。等待确认的过程会阻塞处理器,这会降低处理器的性能。因为这个等待远远比一个指令的执行时间长得多。

Store Bufferes
为了避免这种CPU运算能力的浪费,Store Bufferes被引入使用。处理器把它想要写入到主存的值写到缓存,然后继续去处理其他事情。当所有失效确认(Invalidate Acknowledge)都接收到时,数据才会最终被提交。

这么做有两个风险(Store Bufferes的风险):
第一、就是处理器会尝试从存储缓存(Store buffer)中读取值,但它还没有进行提交。这个的解决方案称为Store Forwarding,它使得加载的时候,如果存储缓存中存在,则进行返回。
第二、保存什么时候完成,这个并没有保证。

硬件内存模型
执行失效也不是一个简单的操作,它需要处理器去处理。另外,存储缓存(Store Buffers)并不是无穷大的,所以处理器有时需要等待失效确认的返回。这两个操作都会使得性能大幅降低。为了应付这种情况,引入了失效队列。它们的约定如下:
1、对于所有的收到的Invalidate请求,Invalidate Acknowledge消息必须立刻发送。
2、Invalidate并不真正执行,而是被放在一个特殊的队列中,在方便的时候才会去执行。
3、处理器不会发送任何消息给所处理的缓存条目,直到它处理Invalidate。
即使是这样处理器已经不知道什么时候优化是允许的,而什么时候并不允许。
干脆处理器将这个任务丢给了写代码的人。这就是内存屏障(Memory Barriers)。
写屏障 Store Memory Barrier 是一条高速处理器在执行者之后的指令之前,应用所有已经在存储缓存(store buffer)中保存的指令。
读屏障 Load Memory Barrier 是一条高速处理器在执行任何的加载前,先应用所有已经在失效队列中的失效操作的指令。

你可能感兴趣的:(CPU多级缓存)