内存屏障和CPU缓存
CPU,多级缓存。
L1一般在32k到4096KB,数据缓存,指令缓存。
多核共享一个L3缓存。
缓存同步协议
MESI协议:
- Modified 修改态, 此行的cache行已经被修改过(脏行)
- Exclusive 专有态,此行的cache内容,和主存相同,但是不出现在其他cache中。
- Shared 共享态,这一行的内容等同于主存,但是也存在于其他cache中。
- Invalid,无效态,这一行cache无效。
多个处理器的时候,单个CPU对缓存中的数据进行了改动,需要通知其他cpu,意味着cpu处理要控制自己的读写操作,还有要监听其他CPU发出的通知,从而保证最终一致性。
编译期指令重排
运行时指令重排
- 当CPU写缓存的时候,发现缓存区的区块正在被其他的CPU占用,为了提高CPU的处理性能,可能将后面的读缓存命令优先执行。
- 并非随便重排,需要遵循as-if-serial语义:不管怎么重排序(编译器和处理器为了提高并行度),单线程的程序的执行结果不能被改变,编译器,runtime和处理器都必须遵循as-if-serial语义。
- 也就是说,编译器和处理器不会对存在数据依赖的关系的操作进行重排序。
问题:
- 缓存中的数据和主内存的数据,并不是实时同步的,各个CPU之间的数据也不是实时同步。在同一个时间点,各CPU所看到的同一个内存地址和数据的值,可能是不一致的。
- CPU的as-if-serial语义可以保证单核CPU在自己执行的时候,保证结果正确
- 在多核多线程的过程总,指令逻辑无法分辨因果关联,可能出现乱序执行,导致程序结果运行错误。
内存屏障指令:
写内存屏障:在指令后插入Store Barrier,让写入缓存中的最新的数据更新,写入主内存,让其他线程可见。强制写入主内存,这种显示调用,CPU就不会因为性能考虑,而去对指令进行重排。
读内存屏障:在指令前加入Load Barrier,可以让高速缓存中的数据失效,强制从主内存当中加载数据。强制读取内存内容可以让CPU缓存和主内存保持一致,避免了缓存导致的一致性问题。