了解CPU缓存和内存屏障机制

本片主要介绍CPU在程序性能优化方面做出的努力。

计算机运行过程中,从硬盘中读取一份数据时需要耗费一些时间的,所以我们的电脑引入了内存,程序运行时一些必须加载的信息可以放在内存中,增加CPU读取信息的速度。其实除了内存之外,CPU内部还有一部分空间是作为CPU缓存的,即从内存中频繁读写的数据会被缓存在CPU中,使得程序运行时读取数据更快速。今天我们来一起了解一下,CPU缓存和内存屏障机制的相关知识。

1、是什么

在计算机系统中,CPU高速缓存(英语:CPU Cache)是用于减少处理器访问内存所需平均时间的部件。在金字塔式存储体系中它位于自顶向下的第二层,仅次于CPU寄存器。其容量远小于内存,但速度却可以接近处理器的频率。

2、使用目的

缓存的出现主要是为了解决CPU运算速度与内存读写速度不匹配的矛盾,因为CPU运算速度要比内存读写速度快很多,这样会使CPU花费很长时间等待数据到来或把数据写入内存。

3、如何起作用

当处理器发出内存访问请求时,会先查看缓存内是否有请求数据。如果存在(命中),则不经访问内存直接返回该数据;如果不存在(失效),则要先把内存中的相应数据载入缓存,再将其返回处理器。

4、多级缓存

CPU缓存被引入之后,随着人们的使用越来越多,缓存被设计为多级缓存。不同级别的缓存的实际意义也不相同,访问顺序也不同。

img

L1 Cache (一级缓存)是CPU第一层高速缓存, 分为数据缓存和指令缓存, 一般服务器CPU的L1缓存的容量通常在32-4096kb
L2 Cache (二级缓存) 由于L1高速缓存的容量限制, 为了再次提高CPU的运算速度, 在CPU外部放置一高速缓存存储器, 即二级缓存
L3 Cache(三级缓存)现在都是内置的, 而它的实际作用既是, L3缓存的应用可以进一步降低内存延迟, 同时提升大数据量计算时处理器的性能. 具有较大L3缓存的处理器 拥有 更有效的文件系统缓存行为  及较短消息和处理器队列长度.

注意:每个CPU都会有L1L2L3,但是L3是多多核公用的。

CPU在读取数据时, 先在L1中寻找, 再从L2中寻找, 再从L3中寻找, 然后是内存, 最后是外存储器

那么我们应该思考,多个CPU从内存中读取同样的数据进行缓存,运行过程中进行了不同的运算之后,最终写入 内存到底以哪个为主呢?在这种高速缓存回写的场景下, 有一个缓存一致性协议, 多数CPU厂商对它进行了实现.
即MESI协议, 它规定每条缓存有个状态位, 同时定义了下面四种状态:

修改态(Modified) 此cache行已被修改过(脏行), 内容已不同于主内存, 为此cache专有
专有态(Exclusive) 此cache行同于主存, 但它不出现于其他cache中
共享态(Shared) 此cache行同于主存, 但也出现于其他cache中
无效态(Invalid) 此cache行无效(空行)

这个协议的本质,其实就是规定了:多个CPU进行缓存处理时, 单个CPU对缓存中的数据进行了改动, 需要通知给其他CPU, 也就意味着, CPU处理要控制自己的读写操作, 还要监听其他CPU发出的通知, 从而保证最终一致。自己埋头干活的同时,多多关注一下别的人~

这是CPU提高性能的第一个手段,还有另一个手段

5、指令重排

img

指令重排的场景: 当CPU写缓存时发现缓存区块正被其它CPU占用, 例如三级缓存是多个CPU共享的区域,如果说当前多个CPU都在抢着L3进行写入,那么我这个CPU要想写入的话就得一直等待,等待所有其他的人都写完了,我才能进行我的写入。针对这种情况,为了提高CPU处理性能, 可能将后面的读缓存命令优先执行.这就是指令重排的背景。

当然也并非随便重排, 需要遵循as-if-serial语义
as-if-serial语义的意思指: 不管怎么重排序, 程序的执行结果不能被改变。编译器, runtime和处理器都必须遵守as-if-serial语义, 不能改变结果

也就是说, 编译器在重排序之前会去分析一下,而不是随意的就去重排,所以实际场景中编译器和处理器不会对存在数据依赖关系的操作做重排序

6、引发思考?

经过上面我们的分析和理解我们应该思考两个问题:

(1)、CPU高速缓存下有一个问题:

缓存中的数据 与 主内存的数据 并不是实时同步的, 各CPU间缓存的数据也不是实时同步. 在同一时间点, 各CPU所看到的同一内存地址的数据的值可能是不一致的.  这属于线程安全问题。

(2)、CPU执行指令重排序优化的一个问题:

虽然遵守了as-if-serial语义, 但仅在单CPU自己执行的情况下能保证结果正确. 多核多线程中, 指令逻辑无法被分辨清除因果关联,因此就可能出现乱序执行, 导致程序运行结果错误

这个第二个我还不是很理解为什么会出现错乱,是多个CPU共同执行一个一百行的代码,一个CPU执行三十行,另一个CPU可能执行70行这样的情况吗?有点不太理解,后续在好好思考一下这个问题

7、CPU厂商提出的解决方案--内存屏障

处理器提供了两个内存屏障指令(Memory Barrier)用于解决上述两个问题:
写内存屏障(Store Memory Barrier): 在指令插入Store Barrier, 能让写入缓存中的最新数据更新写入主内存, 让其他线程可见
强制写入主内存, 这种显示调用, CPU就不会因为性能考虑而进行指令重排

也就是说,如果在缓存中对数据有些的操作的话,就必须先执行这个写,并且写完之后需要更新到内存中去。此时指令重排就不会存在了,就算是你要等待别人写完再写,那你也得先执行写操作,在执行读的操作。

读内存屏障(Load Memory Barrier): 在指令插入Load Barrier, 可以让高速缓存中的数据失效, 强制从新从主内存读取数据
强制读取主内存内容, 让CPU缓存和主内存保持一致, 避免了缓存导致的一致性问题

也就是说我们CPU在每次要使用CPU缓存中的数据之前,都必须强制的去主内存中重新获取一下最新的。

那么通过上面的这个内存屏障机制,我们其实就可以知道,对于计算机而言,数据的实时更新,运算的不出错才是最重要的,虽然说我们需要性能的提升,但是如果运算结果都错了那么我们的提升就没有意义了。所以有的时候我们在设计系统架构或者设计一些别的插件的时候,线程安全,数据的一致性是我们必须要考虑的因素!

原创博客,转载请注明出处~

有任何问题欢迎留言一起探讨一起进步~!

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值