yuenshome / yuenshome.github.io

https://yuenshome.github.io
MIT License
81 stars 15 forks source link

CPU术语与基本模型 #119

Open ysh329 opened 4 years ago

ysh329 commented 4 years ago

本文将会介绍CPU相关背景、CPU术语、基本模型。并在该过程补充相关概念与内容。

中央处理器 (英语:Central Processing Unit,缩写:CPU)是计算机的主要设备之一,功能主要是解释计算机指令以及处理计算机软件中的数据

CPU推动软件运行,因而通常是系统性能分析的首要目标,现代系统一般都为多颗CPU组成,通过内核调度器共享给所有运行软件。当需要的CPU资源超过系统力所能及的范围,进程里的线程或任务将会排队,等待轮候自己运行的机会。(这其中有几个关键词,后面会一一展开)

ysh329 commented 4 years ago

表:时间单位的换算

单位 英文缩写 与1秒的比例
m 60
s 1
毫秒 ms 0.001、1/1000、$1 * 10^{-3}$
微秒 μ s 0.000001、1/1000000、$1*10^{-6}$
纳秒 n s 0.000000001、1/1000000000、$1*10^{-9}$
皮秒 p s 0.000000000001、1/1000000000000、$1*10^{-12}$

等待的过程就会给程序运行带来延时,使得性能下降,为了能感受到延时的影响。下面我们以访问3.3GHz的CPU寄存器的延时为例,下表阐述这些过程的时间量级差别,表中是发生单词操作的时间均值,并等比例放大为我们能感受到的时间(见表格相对时间比例),即假设一次寄存器访问的0.3ns,相当于现实生活中的1秒。

表:系统的各种延时

事件 延时 相对时间比例
1个CPU周期 0.3ns 1s
L1缓存访问 0.9ns 3s
L2缓存访问 2.8ns 9s
L3缓存访问 12.9ns 43s
主存访问(从CPU访问DRAM) 120ns 6分钟
固态硬盘I/O(闪存) 50-150μs 2-6天
旋转磁盘I/O 1-10ms 1-12月
互联网:从旧金山到纽约 40ms 4年
互联网:从旧金山到英国 81ms 8年
互联网:从旧金山到澳大利亚 183ms 19年
TCP包重传 1-3s 105-317年
OS虚拟化系统重启 4s 423年
SCSI命令超时 30s 3000年
硬件虚拟化系统重启 40s 4000年
物理系统重启 5m 32000年

在剖析检查CPU的使用情况,找性能改进空间时,从不同角度可以获取不同的信息:

1. CPU术语

ysh329 commented 4 years ago

2. 基本模型

下面,将用简单模型演示CPU和CPU的基本原理,首先从CPU架构开始。

2.1 CPU架构

下图是一个CPU架构的示例,单个处理器内共有四个核和八个硬件线程。

硬件线程:前面介绍概念的时候说过,它是一种支持在一个核上同时执行多个线程(包括Intel的超线程技术)的CPU架构,每个线程是一个独立的CPU实例。这种扩展的方法又称为多线程。

图:CPU架构 image

该图左侧显示物理架构,右边是从操作系统角度看到的景象。

每个硬件线程可按逻辑CPU寻址(即操作系统看来的角度),因此这个处理器看上去有八块CPU。因为本身物理上的拓补结构,操作系统还会根据额外的这些信息,如哪些CPU在同一个核上,来提高调度器(把CPU分配给线程运行的内核子系统)的调度质量。

2.2 CPU内存缓存

为提高I/O性能,处理器提供多种硬件缓存。下图展示缓存大小的关系,越小则速度越快(平衡),且越靠近CPU。

图:CPU缓存大小 image

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

缓存之所以有效,主要是因为程序运行时对内存的访问呈现局部性(Locality)特征。这种局部性既包括空间局部性(Spatial Locality),也包括时间局部性(Temporal Locality)。有效利用这种局部性,缓存可以达到极高的命中率。

在处理器看来,缓存是一个透明部件。因此,程序员通常无法直接干预对缓存的操作。但是,确实可以根据缓存的特点对程序代码实施特定优化,从而更好地利用缓存。

此外,缓存存在与否,以及是在处理器里集成还是在处理器外,取决于处理器的类型。早期的处理器集成的缓存层次较少。

图:CPU缓存架构 image

一级缓存

一级缓存(Level 1 Cache)简称L1 Cache,位于CPU内核的旁边,是与CPU结合最为紧密的CPU缓存,也是历史上最早出现的CPU缓存。由于一级缓存的技术难度和制造成本最高,提高容量所带来的技术难度增加和成本增加非常大,所带来的性能提升却不明显,性价比很低,而且现有的一级缓存的命中率已经很高,所以一级缓存是所有缓存中容量最小的,比二级缓存要小得多。

通常一级缓存分为一级数据缓存(Data Cache,D-Cache)和一级指令缓存(Instruction Cache,I-Cache)。二者分别用来存放数据以及对执行这些数据的指令进行即时解码,而且两者可以同时被CPU访问,减少了争用Cache所造成的冲突,提高了处理器效能。大多数CPU的一级数据缓存和一级指令缓存具有相同的容量,

二级缓存

二级缓存是CPU性能表现的关键之一,在CPU核心不变化的情况下,增加二级缓存容量能使性能大幅度提高。而同一核心的CPU高低端之分往往也是在二级缓存上有差异,由此可见二级缓存对于CPU的重要性。

从理论上讲,在一颗拥有二级缓存的CPU中,读取一级缓存的命中率为80%。也就是说CPU一级缓存中找到的有用数据占数据总量的80%,剩下的20%从二级缓存中读取。由于不能准确预测将要执行的数据,读取二级缓存的命中率也在80%左右(从二级缓存读到有用的数据占总数据的16%)。那么还有的数据就不得不从内存调用,但这已经是一个相当小的比例了。较高端的CPU中,还会带有三级缓存,它是为读取二级缓存后未命中的数据设计的—种缓存,在拥有三级缓存的CPU中,只有约5%的数据需要从内存中调用,这进一步提高了CPU的效率。

LRU算法

为了保证CPU访问时有较高的命中率,缓存中的内容应该按一定的算法替换。一种较常用的算法是“最近最少使用算法”(LRU算法,Least recently used),它是将最近一段时间内最少被访问过的行淘汰出局。因此需要为每个被访问内容行设置一个计数器,LRU算法是把命中行的计数器清零,其他各行计数器加1。当需要替换时淘汰行计数器计数值最大的数据行出局,其计数器清零过程可以把一些频繁调用后再不需要的数据淘汰出缓存,提高缓存的利用率。

三级缓存

三级缓存是为读取二级缓存后未命中的数据设计的—种缓存,在拥有三级缓存的CPU中,只有约5%的数据需要从内存中调用,这进一步提高了CPU的效率。

L3 Cache(三级缓存),分为两种,早期的是外置,截止2012年都是内置的。而它的实际作用即是,L3缓存的应用可以进一步降低内存延迟,同时提升大数据量计算时处理器的性能。而在服务器领域增加L3缓存在性能方面仍然有显著的提升。

比方具有较大L3缓存的配置利用物理内存会更有效,故它比较慢的磁盘I/O子系统可以处理更多的数据请求。具有较大L3缓存的处理器提供更有效的文件系统缓存行为及较短消息和处理器队列长度(前面提到的概念运行队列:一个等待CPU服务的可运行线程队列)。

2.3 CPU运行队列

前文在术语部分,介绍了CPU运行队列的概念,即一个等待CPU服务的可运行线程队列。不过在讲述运行队列概念前,我们先看一下进程生命周期。

下图展示简化的进程生命周期示意图,其中包含的状态有:

图:进程生命周期 image

根据上面的状态概念,下图展示了一个内核调度器管理的CPU运行队列。

图:CPU运行队列 image

其中,正在排队的R状态和就绪运行的软件线程数量是一个表示CPU饱和度的性能指标。在这一瞬间,有4个排队线程,加上一个在CPU上运行的线程。其中,花在等待CPU运行上的时间又被称为运行时延或者分发器队列延时。

对于多处理器系统,系统内核为每个CPU都提供了各自的运行队列,并尽量使线程被放到同一队列中,这意味着线程更有可能在同一个CPU上运行,因为CPU缓存保存了他们的数据。且这些缓存也被称为热缓存,这种选择运行CPU的方法称为CPU关联

在NUMA系统中,这会提高内存局部或者本地性(Locality),通过有效利用这种局部性(CPU访问内存的速度与节点的距离有关,CPU访问本地节点的内存速度最快),缓存可以达到极高的命中率。

NUMA架构

也称非一致存储访问结构(NUMA:Non-Uniform Memory Access),把一台计算机分成多个节点,每个节点内部拥有多个CPU,节点内部使用共有的内存控制器,节点之间是通过互联模块进行连接和信息交互。

因此节点的所有内存对于本节点所有的CPU都是等同的,对于其他节点中的所有CPU都不同。

因此每个CPU可以访问整个系统内存,但是访问本地节点的内存速度最快(不经过互联模块),访问非本地节点的内存速度较慢(需要经过互联模块),即CPU访问内存的速度与节点的距离有关

在NUMA系统中,通过提高CPU对本地内存的使用,从而提高系统性能。这也避免了队列操作的线程同步开销如mutex锁。若运行队列是全局的且被所有CPU共享,这种开销会影响扩展性。

线程同步

当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。

互斥锁mutex

Linux中提供一把互斥锁mutex也称之为互斥量)。每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。

应注意:同一时刻,只能有一个线程持有该锁

所以,互斥锁实质上是操作系统提供的一把“建议锁”(又称“协同锁”),建议程序中有多线程访问共享资源的时候使用该机制。但,并没有强制限定。 因此,即使有了mutex,如果有线程不按规则来访问数据,依然会造成数据混乱。