现代先进的计算机已是一个软硬件都非常庞大复杂的系统,计算机系统的性能优化也就可以在硬件系统和软件系统的范畴分别实施。本章只针对计算机硬件系统做些简单理论介绍。
硬件的性能优化
微处理器所用的一个时钟周期时间T,其倒数1/T就是时钟频率f(基本单位是Hz),是考量微处理器性能的重要指标。可以说时钟频率和运算位宽是微处理器性能的两个重要指标,除此之外还有一些其他细节对性能有影响。应该看清楚的是,计算机性能优劣评判的根本出发点是处理数据的流量大小及速度;此外,功耗也是便携式计算机的另一个重要指标。
计算机是一个系统,需要所有的外设都不至于拉后腿,才能互相配合出一套各个环节性能均衡的系统。围绕核心指标,又全盘综合考虑外设的物理局限、用户的使用习惯和数据流量的规律,在计算机数据流转的各个环节都提高性能的方法就被一一设计出来。
在物理实现和成本的各种因素综合考虑下,现有的计算机性能优化手段在硬件范围内,并不仅仅局限于提高时钟频率和加大位宽,而是尽可能的多设计一些数字逻辑电路来专门提升某部分的性能,这也是从减轻微处理器的负担来提升整体性能的一种重要思路。换句话说,计算机硬件性能优化的根本思路,就是在某些性能的瓶颈之处,用硬件来代替软件,即用硬件逻辑实现原本需CPU执行软件来实现的功能,从而为CPU减负。在软件层面优化计算机性能也很重要,只是暂不在本章介绍。
计算机或者SoC的架构中流转数据按速度从高速到低速的顺序来排,应当是CPU核心 > 总线 > 外设控制器 > 存储器 > 片外总线 > 外围输入输出设备。每个环节的速度差距都以数量级为基本单位计。其中以从CPU到存储之间的环节来说,存储器的时钟频率低于微处理器的时钟频率,为了弥补这种差距,人们设计了专用缓存来作为辅助:Cache。以存储器读写环节来说,大块存储数据的搬运是计算机中很常见的一种场景,人们设计了专业数字逻辑电路来作为辅助:DMA。又以输出环节来说,为了提高图形图像数据处理速度和显示速度,人们设计了一种专门处理该类数据的数字逻辑电路来作为辅助:GPU。在发现单CPU核的频率提高到一个难以均衡成本及功耗的地步之后,人们回过头来在CPU环节设计了多CPU核。
下面分别介绍这四类硬件。
DMA控制器
DMA (Direct Memory Access,直接内存存取),DMA是专门用来在总线上搬运数据的逻辑硬件,而不需要通过CPU(这里的CPU特指运算器和译码器)。换句话说,在CPU忙于执行指令的时候,DMA可以单独完成将数据从一个地址空间复制到另外一个地址空间。这就在很大程度上分担了CPU的部分工作,比如这片内存上的一片数据传到另一片内存空间上,或者从特定缓存直接传到某些设备上,无论这些设备是否速度相同,都不再需要CPU中断,只要这些设备挂到了总线上,可以被DMA逻辑所传递到就可以用DMA传输。
DMA控制器往往有一套寄存器,可以由CPU进行配置,然后预设好特定的触发条件,只要条件满足,DMA就自动进行传输。在DMA传输的过程中,也可以有控制逻辑设计好按特定单位为数据包传输,其中可能可以自动暂停,在传输完成后,可以有状态位查询或者中断的方式通知CPU。
DMA逻辑硬件没有行业标准,各种厂商在设计DMA的时候,往往按照自家芯片的架构和各种场景需求自行设计,从内部架构到接口寄存器都可能完全不同。但是,DMA的状态机流程和功能概念基本相同,因此底层的不同都可以在驱动软件层进行屏蔽,只给应用软件层统一的函数接口。所以DMA的细节只要去读芯片的数据手册中关于DMA逻辑控制器的寄存器和架构描述就可以了解。
协处理器MMU与cache
MMU是Memory ManagementUnit的缩写,中文名是内存管理单元,是一种复杂的硬件逻辑电路。这一套集成逻辑电路作为一个单元,主要功能就是管理内存访问。在微处理器架构中,可以把总线和内存看做是MMU的下游;那么只要有任何逻辑电路在MMU的上游,需要通过MMU才能访问到总线和内存的,都被MMU管理了内存和总线访问的权限和地址。一般就是把CPU设计成MMU的上游。
MMU可以限制内存访问(访问就是读或者写的意思)权限,也可以做虚拟地址到物理地址的转换(这种地址转换也称为地址映射),MMU的上游访问的地址(虚拟地址)可以被MMU改成另一个地址(实际存在的物理内存地址)去访问内存。
MMU已经是现代微处理器中一个标配的重要部分,MMU的一些基本概念和背景知识是了解计算机体系知识中不可或缺的部分,MMU的功能也是现代计算机体系中重要的部分。MMU不仅是额外设计的硬件,为配合或者说是最大程度发挥MMU的功效,从MMU的驱动程序到操作系统内存使用划分、甚至编译链接系统都要做出相应的改变,所以可以说这巨大的改变已经使MMU是计算机软硬件系统中紧密不可或缺的一部分。甚至有人以是否支持MMU做内核态与用户态内存权限划分功能为标志来判定操作系统软件的先进程度,支持MMU就是现代操作系统,不支持MMU就是低级的小微嵌入式系统。
有了MMU之后,所有MMU上游电路单元访问MMU的地址被称为虚拟地址,MMU访问下游的内存地址被称为物理地址,MMU内部有一套逻辑和地址映射表来做虚拟地址和物理地址的转换,地址映射表(也称为MMU的页表)可以按设计需求放在RAM的某段连续空间内,但所在内存的起始地址会在MMU的某寄存器中有备份,MMU就可以用硬件逻辑按图索骥来访问页表。MMU有最小访问内存单元的限制,即分页机制,目前常见页大小是4Kbyte(这个值不是绝对固定的),MMU的上下游都是按整数个页单位的大小来传输数据。
MMU通常只是以硬件逻辑电路形式在CPU芯片内部作为一个协处理器存在,通常可以根据芯片的架构不同有其自有的指令集和寄存器。比如在ARM核心的芯片中,MMU的驱动软件都以汇编为主,C代码为次。
Cache翻译成中文就是高速缓存,这里是讨论硬件形式的CPU Cache,其内部核心存储单元一般是用六个MOS管搭的静态内存SRAM。Cache可以按需要设计在芯片的各个角落,在此只讨论架设在CPU和总线或内存之间的Cache,芯片其他部位的Cache及软件实现缓存意义的Cache在此不做讨论。此CPU Cache一般和MMU联系紧密,读写速度比总线和内存稍快,比CPU稍慢。根据Cache所处芯片架构位置的不同,有时也可以分为指令Cache(简称Icache)和数据Cache(简称Dcache)。比如在ARM9中,当MMU关闭时,DCache也关闭,但Icache可单独开关,这是ARM设计时所决定的。
Cache通常均分为多个Cache line单位,每条Cache line有自己的标签(Tag)来标记器其状态及其对应的总线地址,Cache内部(或MMU)的逻辑电路会检查标签,然后按既定的替换算法来决定是否要将CPU访问到的地址读取一串数据到Cache line中,CPU无法知晓Cache是读取内存的什么地址及长度,总之能让CPU把指定地址的数据快速的从Cache中读到就是芯片设计师在设计Cache时需要考虑的问题。Cache的大小和Cache line的大小及其地址对齐单位,以及Cache line的替换算法,随着计算机CPU发展一直有持续的改进,现在还大多设计多级Cache,各种Cache的细节日渐繁多,具体的参数可以查CPU的数据手册,在此不一一详述。
虽然Cache对CPU和程序员都是透明的,程序在运行中无法准确知道某时刻Cache中存储的具体数据和具体命中的地址,程序员也不需要对Cache内部有深入的了解,但是Cache的关键指标和理念对程序员有重要意义,设计软件可尽量让需要连续访问的指令或数据都在Cache中被连续的命中。倘若CPU频繁的没有连续命中Cache,也就是说连续的内存访问都需要连续更新对应的一条Cache line,这种现象的名词是Cache颠簸,程序运行性能会大受影响。
尽管Cache在发展的过程中细节繁多,但其读写理念和控制方法一直有较为统一的协议。从MSI协议发展为MESI协议,规定了Cache的状态机及控制方法。MESI协议简洁清晰,是设计Cache和使用Cache非常好的参照物,读者如需专研计算机领域,建议有所了解。
前文介绍过的(美)布莱恩特,奥哈拉伦著作的《深入理解计算机系统》对MMU和Cache有更详细精彩的论述,再次强力推荐。
协处理器GPU
GPU英文全称GraphicProcessing Unit,中文翻译为“图形处理器”。在计算机的输出设备有了彩色电视屏幕之后,图形图像的处理需求及处理技术就开始随着屏幕分辨率的发展而飞速的发展。最终在图形图像显示器的像素和效果越来越高的时候,CPU在刷屏和图像数据运算方面都逐步力不从心。为了加速屏幕显示和特效数据处理,原本给计算机做屏幕显示加速的辅助电路慢慢发展成GPU,GPU朝着增强渲染和纹理等显示效果处理能力的方向在发展,日渐复杂。
在个人电脑上,GPU作为一块集成电路芯片,可以是集成在主板上,和CPU共用内存,也可以做成显卡的核心,有自己独立的内存,也有NVIDIA公司在努力将CPU和GPU进行合并成一颗SoC,号称APU。目前,通常只在平板电脑或者手机上,GPU作为CPU的一个协处理器,和CPU集成在同一块SoC上,有自己的指令集和寄存器,只处理X,Y二维数组数据。由于高像素和高色彩所需数据量较大,其整帧(一个满显示屏的图像称为一帧图像)图像所需的缓存也就较大,在高级芯片中需要做多帧透明重合和特效所需的浮点运算等功能的时候,GPU往往需要较大的内存。为了加速GPU访问内存,在芯片设计时可以和CPU分别有各自的内存,甚至不计成本拥有各自独立的MMU和Cache,这样就可以避免共用总线和内存通道的抢占造成的延时。比如ARM公司的Mali400型号的GPU,就有单独的MMU,其地址和汇编代码自成一体,其代码单独编译成二进制码之后,可由CPU加载后传给GPU运行。通常是系统在需要的时候,由运行在CPU上的上层应用程序调用openGL的接口函数,通过CPU和GPU进行通讯,再由GPU完成各种指定的特效。
协处理器数据处理
在某些需要特殊数据处理的领域,通常可以用专用数字逻辑电路来加强数据处理能力,比如用DSP处理音视频或者用FPGA实现专有数字逻辑等。更进一步也可将FPGA设计成芯片,或者将某专有数字逻辑集成到SoC芯片中,作为一个CPU之外的协处理器。再比如在目前最先进的平板电脑和手机上,已经有考虑设计专用的协处理器来处理大量传感器数据,这使得手机芯片或者平板电脑芯片的能力变得更为强大,系统性能的多方面平衡性更好,没有系统某处短板或瓶颈。
像DSP或FPGA类的协处理器,通常不会挂到片内总线上,而是作为一个较为独立的子系统,和CPU核心通过一些专用总线进行通讯。此类专用总线可以是串口,IIC,SPI,SDIO等等其中一种,速度肯定不如片内总线,一般够用即可,具体使用那种总线可视数据流量需求而定。如果速率不够用,也可以不计成本的设计更高速的专用总线。
多核如何协同工作
在单CPU核心的时钟频率提升到一定地步的时候(大概3GHz时钟频率),其成本和功耗都较高,对性能提升的效果却明显不如以往,边际效应逐步显现。此时时钟频率较低(大概1GHz时钟频率)的多CPU核心却能在各个指标上完败高频单核,所以多核技术得到快速发展。
多CPU核可以同时处理多个任务(每个CPU在同一时间处理一个任务),但多个任务之间不可避免的会有些共同资源,因此多核的协同工作是多核技术的关键。
首先多核要面对的问题就是Cache一致性问题,多核如果共用同样的Cache,多核运行不同的任务需要访问不同的地址,不同的地址共用同一条Cache line的话,可想而知会有严重的Cache颠簸问题。因此多核必定是多Cache,每个核有自己的Cache,当多核的多条Cache line都对应了同样内存地址的时候,就需要一种机制来保证各条Cache line都保持一致性,只要那块内存地址的数据被某核改变了之后,其他核对应的Cache line要能知道这个改变并且重新加载该内存上的数据。按照MESI协议,此类问题已经可以解决。
其次,多核要面对中断分配和处理的问题,哪些中断应该分配给那些CPU核。一般而言,系统的多中断甚至中断嵌套首先由一个统一的中断管理硬件逻辑来处理,然后软件再做相应的配合。
最后在软件层面,操作系统需要配合多核处理器做许多软件资源的并发互斥,防止多核的并发运行程序造成资源抢占或不同步问题。对于操作系统来说,要保证这一点并不轻松,需要程序员高超的编程技术以及艰难的调试。
功耗如何降低
降低功耗说来容易做起来难,即便说起来容易,在此也只能简单的介绍,难以概全。简而言之,降低功耗的思路就是在可能的情况下,将某些没有用到的硬件关闭供电或者维持在一个低耗电的状态,等到需要用这部分硬件电路的时候,又重新供电,让该硬件恢复正常运行状态。
因此,降低功耗的方法因场景而异,首先硬件的设计要能满足各种场景下的省电需求,然后软件才能再做各种场景下相应的配合。换句话说,就是在降低功耗的这个过程中,首要条件是硬件逻辑要保证各个场景下没有用到的硬件模块可以单独省电,内部的各个电源域(Power domain)要能独立开关,在进出省电模式的时候不会自己紊乱或干扰其他模块;其次是软件要能配合硬件做预备条件设置以及数据备份与恢复的工作。
通常一个系统最省电的状态就是最简单常见的低功耗场景,在这种状态下,往往只维系住最低频率的时钟、某些重要寄存器和存储器的供电即可,其他如高频时钟或协处理器或显示器或其他外围设备等都处于最省电或关闭状态。
在计算机系统的使用过程中,功耗是根据使用情况,一步步降低的。首先可以通过硬件监控到长时间没有操作,就可以通过软件关闭显示器,适度降低系统内各种可能降低的时钟频率(比如CPU频率,总线频率,DDR频率,GPU频率等),再进一步可以让某些协处理器或某些硬件逻辑进入省电模式或者断电,最后可以进入到最省电模式(或叫睡眠模式,Deep sleep mode),即维系住最低频率的时钟,以及某些重要寄存器和重要存储器的供电。
降低功耗做起来难的重要原因是调试条件的限制,并且通常涉及多个模块需要多方紧密配合才能达成目标,若稍有不慎某个模块功耗偏大,则整体功耗都受影响。
在实际的调试工作中,功耗的调试不同于普通的软件逻辑,普通的日志或仿真调试等方法都会因为要省电而不能用了,这些调试手段所依赖的硬件会逐步被关闭。应该说必须在芯片设计时就想好功耗在各个阶段的调试手段,有哪些状态寄存器需要提供,有哪些电信号需要如何引出,如何查找漏电的硬件模块,如何查找冗余的程序运行等等。基于便利的调试方法及软硬件紧密的配合,才能将整个系统的功耗调至最低。
功耗的调试是一项复杂的系统性工程,几乎涉及整个软硬件系统的各个模块,要把许多使用场景都调到最低功耗不是件容易的事情,由此造成的死机或者局部异常也往往费时费力才能解决。具体的调试方法和具体的软硬件系统设计有关,需要工程师有丰富的经验和细致入微的执行力,在本章只能笼统的介绍以供参考。