原来 CPU 为程序性能优化做了这么多

栏目: IT技术 · 发布时间: 4年前

内容简介:本文主要来学习内存屏障和 CPU 缓存知识,以便于我们去了解 CPU 对程序性能优化做了哪些努力。首先来看下 CPU 缓存:CPU 缓存是为了提高程序运行的性能,CPU 在很多处理上内部架构做了很多调整,比如 CPU 高速缓存,大家都知道因为硬盘很慢,可以通过缓存把数据加载到内存里面,提高访问速度,而 CPU 处理也有这个机制,尽可能把处理器访问主内存时间开销放在 CPU 高速缓存上面,CPU 访问速度相比内存访问速度又要快好多倍,这就是目前大多数处理器都会去利用的机制,利用处理器的缓存以提高性能。

本文主要来学习内存屏障和 CPU 缓存知识,以便于我们去了解 CPU 对程序性能优化做了哪些努力。

首先来看下 CPU 缓存:

CPU 缓存

CPU 缓存是为了提高程序运行的性能,CPU 在很多处理上内部架构做了很多调整,比如 CPU 高速缓存,大家都知道因为硬盘很慢,可以通过缓存把数据加载到内存里面,提高访问速度,而 CPU 处理也有这个机制,尽可能把处理器访问主内存时间开销放在 CPU 高速缓存上面,CPU 访问速度相比内存访问速度又要快好多倍,这就是目前大多数处理器都会去利用的机制,利用处理器的缓存以提高性能。

原来 CPU 为程序性能优化做了这么多

多级缓存

CPU 的缓存分为三级缓存,所以说多核 CPU 会有多个缓存,我们首先来看下一级缓存(L1 Cache):

L1 Cache 是 CPU 第一层高速缓存,分为数据缓存和指令缓存,一般服务器 CPU 的 L1 缓存的容量通常在 32-4096 KB。

由于 L1 级高速缓存容量的限制,为了再次提高 CPU 的运算速度,在 CPU 外部放置-高速存储器,即二级缓存(L2 Cache)。

因为 L1L2 的容量还是有限,因此提出了三级缓存, L3 现在的都是内置的,它的实际作用即是, L3 缓存的应用可以进一步降低内存延迟,同时提升大数据量计算时处理器的性能,具有较大 L3 缓存的处理器提供更有效的文件系统缓存行为及较短消息和处理器队列长度,一般是多核共享一个 L3 缓存。

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

如下图所示,CPU 缓存架构中,缓存层级越接近 CPU core,容量越小,速度越快。CPU Cache 由若干缓存行组成,缓存行是 CPU Cache 中的最小单位,一个缓存行的大小通常是 64 字节,是 2 的倍数,不同的机器上为 32 到 64 字节不等,并且它有效地引用主内存中的一块地址。

原来 CPU 为程序性能优化做了这么多

多 CPU 读取同样的数据进行缓存,进行不同运算之后,最终写入主内存以哪个 CPU 为准?这就需要缓存同步协议了:

缓存同步协议

在这种高速缓存回写的场景下,有很多 CPU 厂商提出了一些公共的协议- MESI 协议 ,它规定每条缓存有个状态位,同时定义了下面四个状态:

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

多处理器,单个 CPU 对缓存中数据进行了改动,需要通知给其它 CPU,也就是意味着,CPU 处理要控制自己的读写操作,还要监听其他 CPU 发出的通知,从而保证 最终一致

运行时的指令重排

CPU 对性能的优化除了缓存之外还有 运行时指令重排 ,大家可以通过下面的图了解下:

原来 CPU 为程序性能优化做了这么多

比如图中有代码 x = 10;y = z; ,这个代码的正常执行顺序应该是先将 10 写入 x ,读取 z 的值,然后将 z 值写入 y ,实际上真实执行步骤,CPU 执行的时候可能是先读取 z 的值,将 z 值写入 y ,最后再将 10 写入 x ,为什么要做这些修改呢?

因为当 CPU 写缓存时发现缓存区正被其他 CPU 占用(例如:三级缓存),为了提高 CPU 处理性能,可能将后面的 读缓存命令优先执行

指令重排并非随便重排,是需要遵守 as-if-serial 语义的, as-if-serial 语义的意思是指不管怎么重排序(编译器和处理器为了提高并行度),单线程程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守 as-if-serial 语义,也就是说编译器和处理器 不会对存在数据依赖关系的操作做重排序

那么这样就会有如下两个问题:

  1. CPU 高速缓存下有一个问题:

缓存中的数据与主内存的数据并不是实时同步的,各 CPU(或 CPU 核心)间缓存的数据也不是实时同步。 在同一个时间点,各 CPU 所看到同一内存地址的数据的值可能是不一致的

  1. CPU 执行指令重 排序 优化下有一个问题:

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

如何解决上述的两个问题呢,这就需要谈到 内存屏障

内存屏障

处理器提供了两个 内存屏障(Memory Barrier) 指令用于解决上述两个问题:

写内存屏障(Store Memory Barrier):在指令后插入 Store Barrier ,能让写入缓存中的最新数据更新写入主内存,让其他线程可见。强制写入主内存,这种显示调用,CPU 就不会因为性能考虑而去对指令重排。

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

Java 中也有类似的机制,比如 Synchronizedvolatile 都采用了内存屏障的原理。

总结

本文主要介绍了在提高程序运行性能上,CPU 作出了哪些优化:缓存和运行时指令重排,最后还介绍了内存屏障相关知识。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

你不是个玩意儿

你不是个玩意儿

杰伦·拉尼尔 / 葛仲君 / 中信出版社 / 2011-8 / 35.00元

“你不是个玩意儿。” 这句话当然不是骂人,这是一个宣言。人当然不是玩意儿,不是机器,而是人。 在网络化程度越来越高的今天,我们每个人似乎都有足够的理由,无限欣喜地拥抱互联网。然而,你有没有想过互联网那些不完美的设计却是某种潜在的威胁…… 为什么如此多的暴民在社交网站上争吵不休,很多骂人的脏话我们在现实的人际交往中可能从来不会使用,但在匿名网络环境中却漫天飞舞? 互联网的本质......一起来看看 《你不是个玩意儿》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具