CPU高速缓存与内存屏障

你离开我真会死。 提交于 2020-01-10 00:25:35
1. CPU缓存

cpu高速缓存的由来

  • 在CPU的全部取指令周期中(程序计算),至少需要访问一次存储器(也就是我们所说物理内存上的数据)
  • 通常需要多次访问存储器的取操作数或者保存结果,CPU处理计算的速度明显受限于访问存储器的限制
  • 因此解决方案就是利用局部性原理,在CPU与物理内存之间提供一个容量小并且速度快的存储器,称为高速缓存

高速缓存概述

  • 缓存是分“段”(line)的,一个段对应一块存储空间,大小是 32(较早的 ARM、90 年代 /2000 年代早期的 x86 和 PowerPC)、64(较新的 ARM 和 x86)或 128(较新的 Power ISA 机器)字节
  • 高速缓存包含物理内存部分数据副本
  • cpu读取数据时将会先检查高速缓存中的数据是否存在,存在就返回,不存在就读取物理内存数据

高速缓存和内存

高速缓存与内存.jpg

高速缓存分为L1-L3 Cache

  • L1 Cache: 一级缓存是CPU第一层高速缓存,分为指令缓存和数据缓存,一般服务器的CPU的L1缓存容量在32-4096kb,现在的L1 Cache都不能直接与内存直连传输数据
  • L2 Cache: 由于L1级别高速缓存容量的限制,为了再次提高CPU的运算速度,在CPU外部放置一个高速存储器,即二级缓存
  • L3 Cache: 现在的L3缓存都是内置的,主要是进一步降低内存延迟,提升处理器运算能力,一般是多核共享一个L3缓存

CPU系统架构

cpu架构图.jpg

2. 缓存一致性与MESI协议

单CPU缓存的读与写操作

  • 缓存读操作

    • CPU读取数据时,先在L1中寻找,再从L2中寻找,再从L3中寻找,然后是内存,最后是外存储器(持久化介质)
    • 如果只处理读取操作,那么不论是L1-L3的缓存,都将会和主内存的数据保持一致
  • 缓存写操作

    • 缓存直写:直接透过本级缓存,直接将数据写入到下一级缓存或主内存中,写入成功/失败后将对应的缓存内容也执行更新/丢弃操作,这样缓存中的数据与将与主内存数据一致
    • 缓存回写:将修改本级缓存并记录标志段,通过标志段将数据回写到下一级缓存或主内存中,如果标志段被丢弃也将会先进行一次回收,也保证所有级别的缓存数据保持一致

缓存的一致性协议

  • 多核缓存的写操作问题
    • 场景: 在一个多核且每核都有对应的缓存的处理器进行读写操作
    • 假设有一个CPU缓存了主内存上的某一段数据,在另一个CPU上需要对该内存段的数据进行写操作,此时在写数据的CPU更新了缓存而其他CPU并且更新到缓存,这时候便会产生缓存数据的不一致性

缓存一致性约定

如何解决上述的问题,出现缓存数据不一致的原因如下:
  • 多核CPU都有对应的高速缓存,每核缓存的数据都无法共享
  • 这时候我们会想到就是让缓存能够为多核CPU共享,但出现的问题就是处理器运算能力性能会下降,每次都需要等待其中一个CPU进行写操作之后才能够进行下一步的处理
  • 那么我们的期望就是使用多核缓存,同时也能够让它们运作变得像操作一组缓存一样,那么缓存的一致性协议就是为了这一点而设计解决问题的

缓存的MESI协议

缓存的一致性协议有很多种,比较典型的就是MESI协议,关于MESI协议简述如下:
  • 失效(Invalid)缓存段: 缓存不存在或者已经失效过时
  • 共享(Shared)缓存段: 数据有效,并与主内存以及其他Cache的数据保持一致,用于读缓存操作
  • 独占(Exclusive)缓存段: 数据有效,与主内存的数据保持一致,与S的区别就是在于该处理器处于独占的状态时,其他的cpu缓存将会失效
  • 已修改(Modified)缓存段,属于脏段,表示当前的cpu缓存已经修改过,但是还没有同步到主内存中,为当前的cpu所专有
  • 小结:也就是cpu控制缓存的读写操作,还需要监听其他cpu发生的通知,保证缓存最终数据的一致性

cpu数据读写流程

cpu数据访问.jpg

3. 内存屏障

CPU优化手段:运行时指令重排序

  • 为什么会出现指令重排序
当CPU写缓存时发现区块正被其他CPU占用,为了提高CPU处理性能,可能将后面的读缓存命令优先执行
  • 指令重排原则
重排需要遵循as-if-serial语义规则,即不管怎么进行重排序(编译器和处理器为了提高并行度),(单线程)程序的执行结果不能被改变.编译器/runtime/处理器都必须遵循as-if-serial语义,也就是说编译器和处理器不会对存在数据依赖关系的操作做重排序

CPU高速缓存存在的问题

  • 缓存中的数据与主内存的数据并不是实时同步的,各CPU(或CPU核心)间缓存的数据也不是实时同步的,也就是在同一个时间点,各CPU所看到同一个内存地址的数据的值可能是不一致的
  • 指令重排序存在问题,虽然遵循as-if-serial语义,但是仅仅能保证是在单核CPU下单线程自己执行的情况下保证结果是正确的,如果是多核多线程,指令逻辑无法分辨因果关联,可能会出现乱序,导致程序运行结果出现错误.

内存屏障

  • 定义
是一类同步屏障指令,它使得CPU或编译器在对内存进行操作的时候,严格按照一定的顺序来执行, 也就是说在memory barrier之前的指令和memory barrier之后的指令不会由于系统优化等原因而导致乱序
  • 内存屏障指令
    • 写内存屏障,在指令后插入Store Barrier,能让写入缓存中最新的数据更新写入主内存,让其他线程可见.强制写入主内存,这种显示调用,CPU就不会因为性能考虑而去对指令重排
    • 读内存屏障,在指令前插入Load Barrier,可以让高速缓存中的数据失效,强制从新主内存中加载数据读取主内存内容,让CPU缓存与主内存保持一致,避免缓存导致的一致性问题
    • 完全内存屏障,保障了早于屏障的内存读写操作的结果提交到内存之后,再执行晚于屏障的读写操作
  • 作用
    • 就是解决上述CPU高速缓存存在的问题
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!