完全搞懂 CPU 中的内存结构

今天主要就是介绍一下 CPU 中的多级缓存和乱序执行优化,为后面学习多线程作铺垫。先来理解一下 CPU 的结构,后面再说 Java 虚拟机的内存模型。编程

 

先放两张图看一下 CPU 和各级缓存、内存、硬盘之间的关系。缓存

 

 

下面就来介绍一下为何会出现多级缓存,以及会出现什么问题,CPU 又是如何解决的。多线程

 

为何会出现多级缓存呢?说的简单一点由于 CPU 的频率太快了,而如果没有缓存,直接读取内存中的数据又太慢了,咱们不想让 CPU 停下来等待,因此加入了一层读取速度大于内存但小于 CPU 的这么一层东西,这就是缓存。架构

 

加入缓存以后,CPU 须要数据就问缓存要,缓存没有就从主存中读取,并保留一份在缓存中。下次读取就从缓存中读取,加快速度。编程语言

 

可是,咱们常常听到 CPU 运行速度快,缓存次之,而内存慢一点,硬盘最慢。这又是为何呢?性能

 

CPU 运行的快,那是由于一个时钟周期短,一个时钟周期是指机器码0和1变化,实质就是电信号一高一低之间所用的时间,这可都是电信号啊,电的速度能不快嘛!也就 10 纳秒左右吧,1 秒等于 10 的 9 次方纳秒。学习

 

下面介绍一下缓存和内存优化

 

目前缓存基本上都是采用 SRAM 存储器,SRAM 是英文 Static RAM 的缩写,它是一种具备静志存取功能的存储器,不须要刷新电路即能保存它内部存储的数据。不像 DRAM 内存那样须要刷新电路,每隔一段时间,固定要对 DRAM 刷新充电一次,不然内部的数据即会消失,所以 SRAM 具备较高的性能,可是 SRAM 也有它的缺点,即它的集成度较低,相同容量的 DRAM 内存能够设计为较小的体积,可是 SRAM 却须要很大的体积,这也是目前不能将缓存容量作得太大的重要缘由。这中间也解释了为何内存中的数据一断电就没有了。线程

 

而 RAM(随机读写存储器)的工做原理大体是当 CPU 读取主存时,将地址信号放到地址总线上传给主存,主存读到地址信号后,解析信号并定位到指定存储单元,而后将此存储单元数据放到数据总线上返回给 CPU。设计

 

磁盘慢就慢在它的读取是须要借助于磁头移动,这有寻址的过程,而这仍是一个机械运动的过程,上面慢也是在和电信号打交道,而磁盘不只须要电还须要摆臂,因此,最慢的就是它了。

 

再说一点,咱们说的主存就是咱们常说的内存,比方说个人电脑的内存是 12 G,磁盘的空间是 1 T。

 

整理一下就是,咱们为了提升 CPU 的利用率,添加了多级缓存,可是呢,数据的读取和保存都要在主存上进行,如果单线程是没有问题的,一条路走下去,该读读该写写。

 

可是在多线程的状况下就会出现问题,由于每一个线程都有本身的缓存,假如线程 1 从主存中读取到 x,并对其加 1 ,此时尚未写回主存,线程 2 也从主存中读取 x ,并加 1 ,它们是不知道对方的,也不能够读取对方的缓存。这时都将 x 写回主存,那此时 x 的值就少了 1 。

 

这也就是多线程状况下带有缓存的问题,数据出现问题了,怎么办呢?不能把缓存去掉吧,这时就须要一种协议,来保证不一样的线程在读写主存数据时遵照某种规则以保证不会出现数据不一致的问题。这种协议有不少,其中用的比较多的是 MESI 协议,主要用来保证缓存的一致性。

 

MESI 为了保证多个缓存中共享数据的一致性,定义了 cache line 的四种状态,而线程对 cache line 的四种操做可能会产生不一致的状态,所以缓存控制器监听到本地操做和远程操做的时候,须要对地址一致的 cache line 状态进行一致性修改,从而保证数据在多个缓存之间保持一致性。(M: modified  E: Exclusive  S: shared   I: invalid)

 

CPU 中每一个缓存行(caceh line)使用 4 种状态进行标记(使用额外的两位(bit)表示)。

 

M: 被修改(Modified)

 

该缓存行只被缓存在该 CPU 的缓存中,而且是被修改过的(dirty),即与主存中的数据不一致,该缓存行中的内存须要在将来的某个时间点(容许其它 CPU 读取请主存中相应内存以前)写回主存。当被写回主存以后,该缓存行的状态会变成独享(exclusive)状态。

 

E: 独享的(Exclusive)

 

该缓存行只被缓存在该 CPU 的缓存中,它是未被修改过的(clean),与主存中数据一致。该状态能够在任什么时候刻当有其它 CPU 读取该内存时变成共享状态(shared)。一样地,当 CPU 修改该缓存行中内容时,该状态能够变成 Modified 状态。

 

S: 共享的(Shared)

 

该状态意味着该缓存行可能被多个 CPU 缓存,而且各个缓存中的数据与主存数据一致(clean),当有一个 CPU 修改该缓存行中,其它 CPU 中该缓存行能够被做废(变成无效状态)。

 

I: 无效的(Invalid)

 

该缓存是无效的(可能有其它 CPU 修改了该缓存行)。

 

cache line 不一样的状态之间能够相互转化,这也就是 MESI 协议的具体内容,比方说一个处于 M 状态的缓存行必须时刻监听全部试图读该缓存行相对应主存的操做,这种操做必须在缓存将该缓存行写回主存并将状态变成 S 状态以前被延迟执行。

 

还有不少,就不一一说了。

 

再回头看看那个图是否是清晰多了,在 MESI 出现以前的解决缓存一致性的方案是总线锁机制,这种解决方案效率很低,锁住总线期间,其余 CPU 没法访问内存。

 

说完了 CPU 中的多级缓存,再来看看 CPU 中的乱序执行优化,什么意思呢,处理器为提升运算速度而作出违背代码原有顺序的优化。虽然顺序变了,可是执行的结果是不会变的。

 

举个例子,我要去买杯饮料喝,正常逻辑是这样的,去店里->点单->付钱->喝饮料。那其实我还能够在去店里的路上同时就把【点单付钱】一块儿搞定,又或者我先【点单】再去店里付钱拿饮料。

 

类比到 CPU 中也是这样,求 x + y = ? 我计算 x 的值,再计算 y 的值,其实能够一块儿执行,或是先计算 y 的值,固然为了提升运算速度,它会同时计算 x 和 y 的值,一个 CPU 中又不是只有一个逻辑计算的单元。

 

乱序优化就是这样,为了提升效率 CPU 作出的优化,这在单核的时候是不会有问题的,可是在多核时代又会出现问题。

 

再举个例子,线程 1 须要借组一个 flag 变量执行一段逻辑,你能用 C线程 2 来乱序优化一下,先执行逻辑而不看 flag 变量的值?这是确定不行的。

 

行仍是不行确定有一套规则,这套规则也就是内存屏障,过高级了听不懂,换一种说法,就是说不一样架构的处理器在其指令集中提供了不一样的指令来发起内存屏障,对应在编程语言当中就是提供特殊的关键字来调用处理器相关的指令。结果就是有些指令能乱有些则不能!

 

好了,到这里就算把 CPU 级别的特性给说完了,这些知识对于咱们理解 Java 内存模型(JMM) 以及多线程编程颇有用。而 Java 内存模型就是借鉴了硬件的结构。

 

总结一下,在 CPU 中为了提升运行效率,加了多级缓存和乱序执行优化。加了多级缓存以后呢,会出现缓存不一致的状况,解决的办法就是定义了 MESI 等相似协议。对于乱序执行优化带来的问题,CPU 选择内存屏障来解决,即定义了一套指令集,什么样的指令不能执行乱序优化。

 

以上也就是介绍完了硬件的内存架构,看完了 CPU 中的骚操做,是否是火烧眉毛的想要看 Java 中的内存模型了,不要走开,下节更精彩!

相关文章
相关标签/搜索