CPU通常被称为计算机的大脑,就像人的大脑一样,它由几个部分组成,其中包括接收信息的部分,存储信息的部分,处理信息的部分,帮助输出信息的部分等等。这些部分协同工作以处理信息。
在今天的解文章中,我们将介绍构成CPU的关键元素以及它们如何共同为计算机提供动能。
CPU蓝图:ISA
分析任何CPU时,您首先遇到的就是指令集体系结构(ISA)。这是有关CPU如何运行以及所有内部系统如何交互的图形化蓝图。就像同一物种中有许多品种的狗一样,可以在CPU上构建许多不同类型的ISA。两种最常见的类型是x86(在台式机和笔记本电脑中找到)和ARM(在嵌入式和移动设备中找到)。
还有其他一些像MIPS,RISC-V和PowerPC这样的小众应用程序。ISA将指定CPU可以处理哪些指令,如何与内存和缓存交互,如何在多个处理阶段划分工作等等。
为了覆盖CPU的主要部分,我们将遵循一条指令在执行时所采用的路径。不同类型的指令可能遵循不同的路径,并使用CPU的不同部分,但在这里我们将进行概括以涵盖最大部分。我们将从单核处理器的最基本设计开始,并随着我们朝着更现代的设计逐步增加复杂性。
控制单元和数据路径
CPU可以分为两部分:控制单元和数据路径。想象一辆火车。引擎是火车的动力来源,但指挥员却在幕后拉动操纵杆并控制引擎的各个方面。CPU是相同的方式。
数据路径就像引擎一样,顾名思义,是数据在处理过程中流动的路径。数据路径接收输入,处理它们,并在完成后将它们发送到正确的位置。控制单元告诉数据路径如何工作。根据指令,数据路径会将信号路由到不同的组件,打开和关闭数据路径的不同部分,并监视CPU的状态。
指令周期-获取
我们的CPU必须做的第一件事是弄清楚下一步要执行什么指令,然后将它们从内存转移到CPU中。指令由编译器产生,并且特定于CPU的ISA。ISA将共享最常见的指令类型,例如加载,存储,加法,减法等,但是每个特定ISA都有许多其他特殊类型的指令。对于每种类型的指令,控制单元将知道需要将哪些信号路由到何处。
例如,当您在Windows上运行.exe时,该程序的代码将移入内存,并且告诉CPU第一条指令的起始地址。CPU始终维护一个内部寄存器,该寄存器保存要执行的下一条指令的存储器位置。这称为程序计数器(PC)。
一旦知道从哪里开始,指令周期的第一步就是获取该指令。这会将指令从存储器移到CPU的指令寄存器中,这称为提取阶段。实际上,指令可能已经在CPU的高速缓存中,这个我们在后续再介绍。
指令周期-解码
当CPU有一条指令时,它需要专门弄清楚它是什么类型的指令。这称为解码阶段。每个指令将具有一组称为操作码的特定位,该“位”告诉CPU如何解释它。这类似于如何使用不同的文件扩展名告诉计算机如何解释文件。例如,.jpg和.png都是图像文件,但是它们以不同的方式组织数据,因此计算机需要知道类型才能正确解释它们。
根据ISA的复杂程度,CPU的指令解码部分可能会变得复杂。像RISC-V这样的ISA可能只有几十条指令,而x86有数千条指令。在典型的Intel x86 CPU上,解码过程是最具挑战性的过程之一,并且占用大量空间。CPU将解码的最常见的指令类型是存储器,算术或分支指令。
3种主要指令类型
存储指令可能类似于“将值从存储地址1234读入值A”或“将值B写入存储地址5678”。算术指令可能类似于“将值A添加到值B并将结果存储到值C”。分支指令可能类似于“如果C值为正,则执行此代码;如果C值为负,则执行彼代码”。一个典型的程序可能将它们链接在一起,以产生类似“将结果为肯定的内存地址1234的值添加到内存地址5678的值,并将其存储在内存地址4321的结果,如果结果为负的则存储在地址8765的东西” 。
在开始执行刚刚解码的指令之前,我们需要暂停片刻以讨论寄存器。
CPU具有一些很小但非常快的存储器,称为寄存器。在64位CPU上,每个将容纳64位,并且内核可能只有几十个。这些用于存储当前正在使用的值,可以将其视为类似于L0缓存的值。在上面的指令示例中,值A,B和C都将存储在寄存器中。
ALU
现在回到执行阶段。对于我们上面讨论的3种类型的指令,这将有所不同,因此我们将分别介绍每一种。
从算术指令开始,因为它们最容易理解。这些类型的指令被送入算术日志单元(ALU)进行处理。ALU是一种通常具有两个输入和控制信号并输出结果的电路。
想象一下您在中学时期使用的基本计算器。要执行操作,请输入两个输入数字以及要执行的操作类型。计算器进行计算并输出结果。对于我们的CPU的ALU,操作类型由指令的操作码决定,控制单元会将其发送给ALU。除了基本的算术运算之外,ALU还可以执行AND,OR,NOT和XOR之类的bitwise运算。ALU还将为控制单元输出一些有关其刚刚完成的计算的状态信息。这可能包括诸如结果是肯定的,否定的,零的还是溢出的事情。
ALU与算术运算最相关,但是它也可以用于存储器或分支指令。例如,CPU可能需要计算作为先前算术运算结果给出的内存地址。它还可能需要计算偏移量,以添加到分支指令所需的程序计数器中。诸如“如果先前的结果是否定的,则向前跳20条指令”。
内存指令和层次结构
对于内存指令,我们需要了解一个称为“ 内存层次结构”的概念。这代表了高速缓存,RAM和主存储之间的关系。当CPU接收到一条内存指令,该指令针对尚未在其寄存器本地存储的数据时,它将沿内存层次结构下降,直到找到它为止。大多数现代CPU包含三级缓存:L1,L2和L3。CPU首先要检查的地方是L1缓存。这是三级缓存中最小和最快的。L1高速缓存通常分为用于数据的部分和用于指令的部分。请记住,指令需要像数据一样从内存中获取。
典型的L1缓存可能为数百KB。如果CPU在L1缓存中找不到所需的内容,它将检查L2缓存。这可能约为几MB。下一步是L3缓存,它可能是几十MB。如果CPU在三级缓存中找不到所需的数据,它将进入RAM,最后进入主存储器。当我们沿着每一步走时,可用空间大约增加一个数量级,但是等待时间也增加。
CPU找到数据后,它将调出层次结构,以便将来如有需要,CPU可以快速访问它。这里有很多步骤,但是可以确保CPU快速访问所需的数据。例如,CPU可以在一个或两个周期内读取其内部寄存器,在几个周期中读取L1,在十个左右周期中读取L2,在几十个周期中读取L3。如果需要进入内存或主存储,则可能要花费数万甚至数百万个周期。根据系统的不同,每个核心可能会拥有自己的私有L1缓存,与另一个核心共享一个L2,并在四个或更多核心的组之间共享一个L3。我们将在本文后面详细讨论多核CPU。
分支和跳转指令
三种主要指令类型中的最后一种是分支指令。现代程序始终无休止地跳来跳去,CPU很少执行没有分支的连续指令。分支指令来自诸如if语句,for循环和return语句之类的编程元素。这些都用于中断程序执行并切换到代码的不同部分。也有跳转指令,这些跳转指令是始终采用的分支指令。
条件分支对于CPU尤其棘手,因为它可能一次执行多个指令,并且可能直到分支开始执行后续指令后才能确定分支的结果。
为了完全理解为什么这是一个问题,我们需要进行另一种转移,并讨论流水线。指令周期中的每个步骤可能需要几个周期才能完成。这意味着在提取指令时,ALU否则将处于空闲状态。为了最大化CPU的效率,我们在称为流水线的过程中划分每个阶段。
理解这一点的经典方法是以洗衣服为对比。您有两个东西要洗,洗涤和干燥个需要一个小时。您可以将第一个物件放入洗衣机中,然后在烘干机中放入,等烘干之后后再开始洗第二个产品。这将花费四个小时。但是,如果您划分工作并在第一个产品干燥的同时开始第二个洗涤,则可以在三个小时内完成两个装载。一小时减少量取决于您的要洗的东西数量以及洗衣机和烘干机的数量。洗烘每个东西仍然需要两个小时,但是如果计算重叠,则将总吞吐量从0.5个产品/小时增加到0.75个产品/小时。
CPU使用相同的方法来提高指令吞吐量。现代的ARM或x86 CPU可能具有20多个流水线级,这意味着在任何给定点,该内核一次都可以处理20多个不同的指令。每种设计都是唯一的,但是一个样本划分可能是4个周期用于读取,6个周期用于解码,3个周期用于执行以及7个周期将结果更新回内存。
回到分支机构,希望您可以开始看到此问题。如果直到周期10才知道一条指令是分支,那么我们将已经开始执行9条新指令,如果采用该分支,这些指令可能无效。为了解决这个问题,CPU具有非常复杂的结构,称为分支预测器。他们使用机器学习中的类似概念来尝试猜测是否将采用分支。分支预测变量的复杂性远远超出了本文的范围,但是在基本级别上,它们跟踪先前分支的状态,以了解是否可能采用即将到来的分支。现代分支预测器可以具有95%或更高的准确性。
一旦确定分支的结果(已完成流水线的该阶段),程序计数器将被更新,CPU将继续执行下一条指令。如果分支的预测错误,则CPU将在分支错误地开始执行后抛出所有指令,然后从正确的位置重新启动。
乱序执行
现在,我们知道如何执行三种最常见的指令类型,让我们看一下CPU的一些更高级的功能。实际上,所有现代处理器实际上并没有按接收顺序执行指令。等待执行其他指令时,可以使用称为乱序执行的范例来最大程度地减少停机时间。
如果CPU知道即将到来的指令,但所需的数据并不能及时准备,这时候它可以在等待时切换指令顺序并从程序的后面引入一条独立的指令。该指令重新排序是一种功能非常强大的工具,但它远非CPU使用的唯一技巧。
另一个提高性能的功能称为预取。如果您要花时间从头到尾完成一条随机指令需要多长时间,您会发现内存访问大部分时间都用完了。预取器是CPU中的一个单元,它试图预见将来的指令以及它们将需要什么数据。如果发现需要CPU尚未缓存的数据,它将到达RAM并将该数据提取到缓存中。因此它的名称叫做预取。
加速器与未来
在CPU中,另一个正在增加的主要功能是对于特定任务的加速。这些电路的全部工作是尽可能快地完成一项小任务。这可能包括加密,媒体编码或机器学习。
CPU可以自己完成这些事情,但是拥有专用于它们的单元会大大提高效率。专用GPU就是一个很好的例子。CPU当然可以执行图形处理所需的计算,但是为它们配备专用单元可以提供更好的性能数量级。随着加速器的兴起,CPU的实际核心可能只占芯片的一小部分。
下图显示了几年前的Intel CPU。大部分空间被内核和缓存占用。下面的第二张图片是新的AMD芯片。那里的大部分空间都由核心以外的组件占用。
走向多核
最后要介绍的主要功能是如何将一堆单独的CPU连接在一起以形成多核CPU。这不只是简单地放入我们之前讨论的单核设计的多个副本那样简单。就像没有简单的方法将单线程程序转换为多线程程序一样,相同的概念也适用于硬件。问题来自核心之间的依赖性。
例如,对于4核设计,CPU需要能够以4倍的速度发出指令。它还需要四个单独的内存接口。由于多个实体可能会处理相同的数据,因此必须解决诸如一致性和非一致性之类的问题。如果两个内核都使用相同的数据来处理指令,那么他们如何知道谁拥有正确的价值?如果一个内核修改了数据但又没有及时到达另一个内核执行该怎么办?由于它们具有可存储重叠数据的单独缓存,因此必须使用复杂的算法和控制器来消除这些冲突。
随着CPU中内核数量的增加,正确的分支预测也非常重要。内核一次执行的指令越多,其中一个处理分支指令的可能性就越高。这意味着指令流可能随时更改。
通常,单独的内核将处理来自不同线程的指令流。这有助于减少内核之间的依赖性。这就是为什么如果您检查“任务管理器”,您会经常看到一个核心在努力工作,而其他核心却在工作。许多程序不是为多线程设计的。在某些情况下,让一个核心来完成工作比支付试图分工的开销要高得多。
物理设计
本文的大部分内容都集中在CPU的体系结构设计上,因为这是大多数复杂性所在。但是,所有这些都需要在现实世界中创建,这又增加了另一层次的复杂性。
为了使整个处理器中的所有组件同步,使用了时钟信号。现代处理器通常在3.0GHz和5.0GHz之间运行,并且在过去十年中似乎没有改变。在每个周期中,芯片内部数十亿个晶体管处于导通和截止状态。
时钟对于确保在流水线的每个阶段前进时,所有值在正确的时间显示都是至关重要的。时钟确定CPU每秒可以处理多少条指令。通过超频提高其频率将使芯片速度更快,但也会增加功耗和热量输出。
热量是CPU的最大敌人。随着数字电子设备的升温,微观晶体管可能开始退化。如果不清除热量,可能会导致芯片损坏。这就是为什么所有CPU都带有散热器的原因。CPU的实际硅芯片可能仅占物理设备表面积的20%。占用空间的增加允许热量更均匀地散布到散热器。它还允许更多的引脚用于与外部组件的接口。
现代CPU的背面可以有数千个或更多的输入和输出引脚。由于大多数计算部件都在芯片内,因此移动芯片可能只有几百个引脚。不管采用哪种设计,其中约有一半专门用于供电,其余用于数据通信。这包括与RAM,芯片组,存储,PCIe设备等的通信。
随着高性能CPU在满负载下汲取100安培或更高的电流,它们需要数百个引脚才能平均分散电流消耗。引脚通常镀金以提高导电性。不同的制造商在其许多产品线中使用不同的销钉布置。
结合一个例子
总结一下,我们将快速介绍一下Intel Core 2 CPU的设计。这是从2006年开始的,因此某些零件可能已过时,但是尚无有关较新设计的详细信息。
从顶部开始,我们有指令缓存和ITLB。转换后备缓冲区(TLB)用于帮助CPU知道存储器中要查找所需指令的位置。这些指令存储在L1指令缓存中,然后发送到预解码器中。x86体系结构极其复杂且密集,因此有许多解码步骤。同时,分支预测器和预取器都在期待由传入指令引起的任何潜在问题。
从那里,指令被发送到指令队列。回顾一下无序设计如何使CPU执行指令并选择最及时的指令来执行。该队列保存CPU正在考虑的当前指令。一旦CPU知道哪一条指令将是最佳执行方式,它将进一步解码为微操作。虽然一条指令可能包含CPU的复杂任务,但微操作是精细的任务,CPU可以更轻松地对其进行解释。
然后,这些指令进入“注册表”,“ ROB”和“保留站”。这三个组成部分的确切功能有些复杂(请考虑研究生水平的大学课程),但是它们在乱序过程中用于帮助管理指令之间的依赖关系。
一个“核心”实际上将具有许多ALU和内存端口。将传入的操作放入预留站,直到可以使用ALU或内存端口。一旦所需的组件可用,该指令将在L1数据缓存的帮助下进行处理。输出结果将被存储,并且CPU现在准备从下一条指令开始。就是这样!
虽然本文并不是要确切地指导每个CPU的工作方式,但是它应该使您对它们的内部工作原理和复杂性有一个很好的了解。坦白说,AMD和Intel之外的人实际上都不知道他们的CPU如何工作。本文的每个部分都代表了整个研究和开发领域,因此此处提供的信息只是从头开始。