加入收藏 | 设为首页 | 会员中心 | 我要投稿 应用网_丽江站长网 (http://www.0888zz.com/)- 科技、建站、数据工具、云上网络、机器学习!
当前位置: 首页 > 综合聚焦 > 创业热点 > 经验 > 正文

高性能服务端系列:处理器篇

发布时间:2016-04-14 13:57:51 所属栏目:经验 来源:阿里百川专区的网站
导读:从JMM说起,作为一名JAVA开发,特别在多线程编程实践中,了解和熟悉JAVA内存模型是很有必要的。刚开始接触内存模型的时候,有很多概念非常陌生,比如happens-before,可见

从JMM说起,作为一名JAVA开发,特别在多线程编程实践中,了解和熟悉JAVA内存模型是很有必要的。刚开始接触内存模型的时候,有很多概念非常陌生,比如happens-before,可见性,顺序性等等。要理解这些关键词,需要先对编译器、处理器的知识有一些了解。

还有一些框架例如disruptor,在设计的时候就考虑了CPU的特点,充分发挥CPU的性能。要理解这类框架,也需要对处理器有一定了解。

introduction

先看下面这个表格,一些场景下的延时,比如CPU执行一条指令大约是1纳秒,从L1 cache获取数据需要0.5纳米,从主存中取数据需要100纳秒等等。

QQ截图20160414094727

经典的RISC pipeline由以下几步组成:

1. 取指令

2. 译指令

3. 执行

4. 内存访问

5. 寄存器回写

由于访问主存的延时和指令执行的延时不在一个数量级,所以CPU一般会使用访问速度更快的缓存,现代处理器的缓存一般是分为三级,下图是一般CPU的缓存结构。每一个CPU核共享L1、L2 Cache,所有的CPU核共享L3 Cache。

高性能服务端系列:处理器篇

cache line

在JVM中,我们都知道对象都是存在于内存堆中的,也就是主存中,而对于CPU来说,它并不关心程序中操作的对象,它只关心对某个内存块的读和写,为了让读写速度更快,CPU会首先把数据从主存中的数据以cache line的粒度读到CPU cache中,一个cache line一般是64 bytes。假设程序中读取某一个int变量,CPU并不是只从主存中读取4个字节,而是会一次性读取64个字节,然后放到cpu cache中。因为往往紧挨着的数据,更有可能在接下来会被使用到。比如遍历一个数组,因为数组空间是连续的,所以并不是每次取数组中的元素都要从主存中去拿,第一次从主存把数据放到cache line中,后续访问的数据很有可能已经在cache中了,

cache hit

CPU获取的内存地址在cache中存在,叫做cache hit。

cache miss

如果CPU的访问的内存地址不在L1 cache中,就叫做L1 cache miss,由于访问主存的速度远远慢于指令的执行速度,一旦发生cache miss,CPU就会在上一级cache中获取,最差的情况需要从主存中获取。一旦要从主存中获取数据,当前指令的执行相对来说就会显得非常慢。

cache associativity

根据内存和cache的映射关系不同,有三种映射方式。

1. direct mapped

direct mapped方式查询最快,因为只有一个坑,只需要比较一次。但是容易发生冲突。

2. n-way set associative

n-way associative是一种折中的方式,可以有较高的缓存命中率,又不至于每次查询比较慢。

3. full associative

只要cache没有满还能把主存中的数据放到cache中,但是查询的时候需要全扫描,效率低。

其实direct mapped和full associative是n-way associative的特殊形式。

下面这张图是我看到的最容易理解的资料。

高性能服务端系列:处理器篇

cache coherency

现在的CPU一般都有多个核,我们知道当某个核读取某个内存地址时,会把这个内存地址附近的64个字节放到当前核的cache line中,假设此时另外一个CPU核同时把这部分数据放到了对应的cache line中,这时候这64字节的数据实际上有三份,两份在CPU cache中,一份在主存中。自然而然就要考虑到数据一致性的问题,如何保证在某一个核中的数据做了改动时,其它的数据副本也能感知到变化呢?是由缓存一致性协议来保证的。缓存一致性协议也叫作MESI协议。简单的来说,就是CPU的cache line被标记为以下四种状态之一。

Modified

当前cache line中的数据被CPU修改过,并且只在当前核对应的cache中,数据还没有被回写到主存中,那么当前cache line就处于Modified状态。如果这个时候其它的核需要读取该cache line中的,需要把当前cache line中的数据回写到主存中去。一旦回写到主存中去后,当前cache line的状态变为Shared

Exclusive

当前cache line只在一个核对应的cache中,数据和主存中的数据一致。如果有另外一个核读取当前cache line,则状态变为Shared,如果当前核修改了其中的数据,则变成Modified状态。

Shared

如果cache line处于Shared状态,则表示该cache line在其它核对应的cache中也有副本,而且这两个副本和主存中的数据一致。

Invalid

如果cache line处于Invalid状态,则表示这块cache line中的数据已经无效了,如果要读取其中的数据的话,需要重新从主存中获取。

只有cache line处于Exclusive或者Modified状态时才能进行写操作。如果处于Shared状态,那么要先广播一个消息(Request For Ownership),invalidate其它核对应的cache line。

如果cache line处于Modified状态,那么需要能探测到其它试图读取该cache line的操作。

如果cache line处于Shared状态,它必须监听其它cache的invalidate信息,一旦其它核修改了对应的cache line,其它cache 中对应的cache line需要变为invalid状态。

MESI协议中有两个行为效率会比较低,

1. 当cache line状态为Invalid时,需要写入数据。

2. 把cache line的状态变为invalid

CPU通过store buffer和invalid queue来降低延时。

当在invalid状态进行写入时,首先会给其它CPU核发送invalid消息,然后把当前写入的数据写入到store buffer中。然后在某个时刻在真正的写入到cache line中。由于不是马上写入到cache line中,所以当前核如果要读cache line中的数据,需要先扫描store buffer,同时其它CPU核是看不到当前核store buffer中的数据的。除非store buffer中的数据被刷到cache中。

对于invalid queue,当收到invalid消息时,cache line不会马上变成invalid状态,而是把消息写入invalid queue中。和store buffer不同的是当前核是无法扫描invalid queue的。

为了保证数据的一致性,这就需要memory barrier了。store barrier会把store buffer中的数据刷到cache中,read barrier会执行invalid queue中的消息。

注意

要保证数据的一致性,仅仅有MESI协议还不够,通常还需要memory barrier的配合。

memory barrier

memory barrier的作用有两个

(编辑:应用网_丽江站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读