这是本系列博客的第二篇,我们来到了整个系列的关键所在。在为新公司或新项目做推介时,风投资本家或高管经常会问一个基本问题:「为什么是现在?」
为了回答这个问题,我们需要简单回顾一下处理器的发展历史,看看近些年这一领域发生了哪些重大变化。
处理器是什么?
简而言之,处理器是计算机系统中负责实际数字计算的部分。它接收用户输入数据(用数值表示),并根据用户的请求生成新的数据,即执行一组用户希望的算术运算。处理器使用其算术单元来生成计算结果,这意味着运行程序。
20 世纪 80 年代,处理器在个人电脑中被商品化。它们逐渐成为我们日常生活中不可或缺的一部分,笔记本电脑、手机和连接数十亿云和数据中心用户的全球基础设施计算结构中都有它们的身影。随着计算密集型应用的日益普及和大量新用户数据的出现,当代计算系统必须满足对处理能力不断增长的需求。因此,我们总是需要更好的处理器。长期以来,「更好」意味着「更快」,但现在也可以是「更高效」,即花同样的时间,但使用的能源更少,碳足迹更少。
处理器进化史
计算机系统的进化是人类最杰出的工程成就之一。我们花了大约 50 年的时间达到了这样一种高度:一个人口袋里的普通智能手机拥有的计算能力是阿波罗登月任务中用到的房间大小的计算机的 100 万倍。这一演变的关键在于半导体行业,以及它如何改进处理器的速度、功率和成本。
英特尔 4004:第一款商用微处理器,发布于 1971 年。
处理器是由被称为「晶体管」的电子元件组成的。晶体管是逻辑开关,用作从原始逻辑函数(如与、或、非)到复杂算术(浮点加法、正弦函数)、存储器(如 ROM、DRAM)等所有东西的构建模块。这些年来,晶体管一直在不断缩小。
1965 年,戈登 · 摩尔发现,集成电路中的晶体管数量每年翻一番(后来更新为每 18-24 个月)。他预计这一趋势将持续至少十年。虽然有人认为,这与其说是一个「定律」,不如说是一个「行业趋势」,但它确实持续了大约 50 年,是历史上持续时间最长的人为趋势之一。
晶体管缩放的电学特性。
但除了摩尔定律,还有一条不那么有名但同样重要的定律。它被称为「登纳德缩放比例定律」,由罗伯特 · 登纳德在 1974 年提出。虽然摩尔定律预测晶体管将逐年缩小,但登纳德问道:「除了能够在单个芯片上安装更多晶体管之外,拥有更小的晶体管还有什么实际好处?」他的观察结果是,当晶体管以 k 为倍数缩小时,电流也会降低。此外,由于电子移动的距离更小,我们最终得到的晶体管快了 k 倍,最重要的是——它的功率下降到 1/k^2。因此,总的来说,我们可以多装 k^2 个晶体管,逻辑函数将快大约 k 倍,但芯片的功耗不会增加。
处理器发展第一阶段:频率时代(1970-2000 年代)
微处理器频率速率的演变。
早期,微处理器行业主要集中在 CPU 上,因为 CPU 是当时计算机系统的主力。微处理器厂商充分利用了缩放定律。具体来说,他们的目标是提高 CPU 的频率,因为更快的晶体管使处理器能够以更高的速率执行相同的计算(更高的频率 = 每秒更多的计算)。这是一种有些简单的看待事物的方式;处理器有很多架构创新,但最终,在早期,频率对性能有很大贡献,从英特尔 4004 的 0.5MHz、486 的 50MHz、奔腾的 500MHz 到奔腾 4 系列的 3–4GHz。
功率密度的演变。
大约在 2000 年,登纳德缩放比例定律开始崩溃。具体来说,随着频率的提升,电压停止以相同的速率下降,功率密度速率也是如此。如果这种趋势持续下去,芯片发热问题将不容忽视。然而,强大的散热方案还不成熟。因此,供应商无法继续依靠提高 CPU 频率来获得更高的性能,需要想想其他出路。
处理器发展第二阶段:多核时代(2000 年代 - 2010 年代中期)
停滞不前的 CPU 频率意味着提高单个应用的速度变得非常困难,因为单个应用是以连续指令流的形式编写的。但是,正如摩尔定律所说的那样,每过 18 个月,我们芯片中的晶体管就会变为原来的两倍。因此,这次的解决方案不是加快单个处理器的速度,而是将芯片分成多个相同的处理内核,每个内核执行其指令流。
CPU 和 GPU 核数的演化。
对于 CPU 来说,拥有多个内核是很自然的,因为它已经在并发执行多个独立的任务,比如你的互联网浏览器、文字处理器和声音播放器(更准确地说,操作系统在创建这种并发执行的抽象方面做得很好)。因此,一个应用可以在一个内核上运行,而另一个应用可以在另一个内核上运行。通过这种实践,多核芯片可以在给定的时间内执行更多的任务。然而,为了加快单个程序的速度,程序员需要将其并行化,这意味着将原始程序的指令流分解成多个指令「子流」或「线程」。简单地说,一组线程可以以任何顺序在多个内核上并发运行,没有任何一个线程会干扰另一个线程的执行。这种实践被称为「多线程编程」,是单个程序从多核执行中获得性能提升的最普遍方式。
多核执行的一种常见形式是在 GPU 中。虽然 CPU 由少量快速和复杂的内核组成,但 GPU 依赖大量更简单的内核。通常来讲,GPU 侧重于图形应用,因为图形图像(例如视频中的图像)由数千个像素组成,可以通过一系列简单且预先确定的计算来独立处理。从概念上来说,每个像素可以被分配一个线程,并执行一个简单的「迷你程序」来计算其行为(如颜色和亮度级别)。高度的像素级并行使得开发数千个处理内核变得很自然。因此,在下一轮处理器进化中,CPU 和 GPU 供应商没有加快单个任务的速度,而是利用摩尔定律来增加内核数量,因为他们仍然能够在单个芯片上获得和使用更多的晶体管。
不幸的是,到了 2010 年前后,事情变得更加复杂:登纳德缩放比例定律走到了尽头,因为晶体管的电压接近物理极限,无法继续缩小。虽然以前可以在保持相同功率预算的情况下增加晶体管数量,但晶体管数量翻倍意味着功耗也翻倍。登纳德缩放比例定律的消亡意味着当代芯片将遭遇「利用墙(utilization wall)」。此时,我们的芯片上有多少晶体管并不重要——只要有功耗限制(受芯片冷却能力的限制),我们就不能利用芯片中超过给定部分的晶体管。芯片的其余部分必须断电,这种现象也被称为「暗硅」。
处理器发展第三阶段:加速器时代(2010 年代至今)
暗硅本质上是「摩尔定律终结」的大预演——对处理器制造商来说,时代变得具有挑战性。一方面,计算需求飞速增长:智能手机变得无处不在,而且拥有强大的计算能力,云服务器需要处理越来越多的服务,「最糟糕的是」——人工智能重新登上历史舞台,并以惊人的速度吞噬计算资源。另一方面,在这个不幸的时代,暗硅成为晶体管芯片发展的障碍。因此,当我们比以往任何时候都更需要提高处理能力时,这件事却变得以往任何时候都更加困难。
训练 SOTA AI 模型所需的计算量。
自从新一代芯片被暗硅束缚以来,计算机行业就开始把精力放到了硬件加速器上。他们的想法是:如果不能再增加晶体管,那就好好利用现有的晶体管吧。具体怎么做呢?答案是:专门化。
传统的 CPU 被设计成通用的。它们使用相同的硬件结构来运行我们所有应用(操作系统、文字处理器、计算器、互联网浏览器、电子邮件客户端、媒体播放器等)的代码。这些硬件结构需要支持大量的逻辑操作,并捕获许多可能的模式和程序诱发的行为。这相当于硬件可用性很好,但效率相当低。如果我们只专注于某些应用,我们就可以缩小问题领域,进而从芯片中去除大量的结构冗余。
通用 CPU vs. 面向特定应用的加速器。
加速器是专门面向特定应用或领域的芯片,也就是说,它们不会运行所有应用(例如不运行操作系统),而是在硬件设计层面就考虑一个很窄的范围,因为:1)它们的硬件结构仅满足特定任务的操作;2)硬件和软件之间的接口更简单。具体来说,由于加速器在给定的域内运行,加速器程序的代码应该更紧凑,因为它编码的数据更少。
举个例子,假如你要开一家餐厅,但面积、用电预算是有限的。现在你要决定这个餐厅具体做哪些菜,是比萨、素食、汉堡、寿司全做(a)还是只做披萨(b)?
如果选 a,你的餐厅确实能满足很多口味不同的顾客,但你的厨师就要做很多菜,而且不见得每种都擅长。此外,你可能还需要买多个冰箱来存储不同的食材,并密切关注哪些食材用完了,哪些变质了,不同的食材还有可能混在一起,管理成本大大提高。
但如果选 b,你就可以雇佣一位*的披萨专家,准备少量的配料,再买一台定制的烤箱来做披萨。你的厨房会非常整洁、高效:一张桌子做面团,一张桌子放酱汁和奶酪,一张桌子放配料。但同时,这种做法也有风险:如果明天没有人想吃披萨怎么办?如果大家想吃的披萨用你定制的烤箱做不出来怎么办?你已经花了很多钱打造这个专门化的厨房,现在是进退两难:不改造厨房就可能面临关店,改造又要花一大笔钱,而且改完之后,客户的口味可能又变了。
回到处理器世界:类比上面的例子,CPU 就相当于选项 a,面向特定领域的加速器就是选项 b,店面大小限制就相当于硅预算。你将如何设计你的芯片?显然,现实并没有那么两极分化,而是有一个类似光谱的过渡区域。在这个光谱中,人们或多或少地用通用性来换取效率。早期的硬件加速器是为一些特定领域设计的,如数字信号处理、网络处理,或者作为主 CPU 的辅助协处理器。
从 CPU 到主要加速应用领域的第一个转变是 GPU。一个 CPU 有几个复杂的处理核心,每个核心都采用各种技巧,比如分支预测器和乱序执行引擎,以尽可能加快单线程作业的速度。GPU 的结构则有所不同。GPU 由许多简单的内核组成,这些内核具有简单的控制流并运行简单的程序。最初,GPU 用于图形应用,如计算机游戏,因为这些应用包含由数千或数百万像素组成的图像,每个像素都可以并行独立计算。一个 GPU 程序通常由一些核函数组成,称为「内核(kernel)」。每个内核都包含一系列简单的计算,并在不同的数据部分(如一个像素或包含几个像素的 patch)执行数千次。这些属性使得图形应用成为硬件加速的目标。它们行为简单,因此不需要分支预测器形式的复杂指令控制流;它们只需要少量操作,因此不需要复杂的算术单元(比如计算正弦函数或进行 64 位浮点除法的单元)。人们后来发现,这些属性不仅适用于图形应用,GPU 的适用性还可以扩展到其他领域,如线性代数或科学应用。如今,加速计算已经不仅仅局限于 GPU。从完全可编程但低效的 CPU 到高效但可编程性有限的 ASIC,加速计算的概念无处不在。
深度神经网络的处理替代方案。来源:微软。
如今,随着越来越多表现出「良好」特性的应用程序成为加速的目标,加速器越来越受关注:视频编解码器、数据库处理器、加密货币矿机、分子动力学,当然还有人工智能。
是什么让 AI 成为加速目标?
商业可行性
设计芯片是一个费力、耗资的事情——你需要聘请行业专家、使用昂贵的工具进行芯片设计和验证、开发原型以及制造芯片。如果你想使用尖端的制程(例如现在的 5nm CMOS),耗资将达到数千万美元,不论成功或失败。幸运的是,对于人工智能来说,花钱不是问题。AI 的潜在收益是巨大的,AI 平台有望在不久的将来产生数万亿美元的收入。如果你的想法足够好,你应该能够很容易地为这项工作找到资金。
AI 是一个「可加速」的应用领域
AI 程序具有使其适用于硬件加速的所有属性。首先最重要的是,它们是大规模并行的:大部分计算都花在张量运算上,如卷积或自注意力算子。如果可能,还可以增加 batch size,以便硬件一次处理多个样本,提高硬件利用率并进一步推动并行性。硬件处理器驱动其快速运行能力的主要因素是并行计算。其次,AI 计算仅限于少数运算种类:主要是线性代数核的乘法和加法、一些非线性算子,例如模拟突触激活的 ReLU,以及基于 softmax 的分类的指数运算。狭窄的问题空间使我们能够简化计算硬件,专注于某些运算符。
最后,由于 AI 程序可以表示为计算图,因此我们可以在编译时知道控制流,就像具有已知迭代次数的 for 循环一样,通信和数据重用模式也相当受限,因此可以表征我们需要哪些网络拓扑在不同计算单元和软件定义的暂存存储器之间通信数据,以控制数据的存储和编排方式。
AI 算法是以硬件友好的方式构建的
不久之前,如果你想在计算架构领域进行创新,你可能会说:「我有一个新的架构改进的想法,它可以显著地提高一些东西,但是——我需要做的就是稍微改变编程界面并让程序员使用这个功能。」在那个时候这种想法会行不通。程序员的 API 是不可触及的,而且用破坏程序「干净」语义流的低级细节来加重程序员的负担是很难的。
此外,将底层架构细节与面向程序员的代码混合在一起并不是一个好习惯。首先它是不可移植的,因为某些架构特征在芯片代际之间发生变化。其次它可能会被错误地编程,因为大多数程序员对底层硬件没有深入的了解。
虽然你可以说 GPU 和多核 CPU 已经因为多线程(有时甚至是——内存墙)偏离了传统的编程模型,但由于单线程性能早已不是指数级增长,我们只能将希望诉诸于多线程编程,因为这是我们唯一的选择。多线程编程仍然很难掌握,需要大量的教育。幸运的是,当人们编写 AI 程序时,他们会使用神经层和其他定义明确的块来构建计算图。
高级程序代码(例如 TensorFlow 或 PyTorch 中的代码)已经在以一种可以标记并行块并构建数据流图的方式编写。因此理论上,你可以构建丰富的软件库和足够精细的编译器工具链来理解程序的语义并将其有效地降为硬件表示,而无需开发应用程序的程序员做任何参与,让数据科学家做他们的事情,他们可以不在乎任务在哪些硬件上运行。在实践中,编译器完全成熟还需要时间。
几乎没有其他选择
人工智能无处不在,大型数据中心、智能手机、传感器,机器人和自动驾驶汽车中都有它的身影。每个系统都有不同的现实限制:人们肯定不愿意自动驾驶汽车因为算力太小而无法检测障碍物,也不能接受因为效率低而在训练超大规模预训练模型时每天多花数千美元,AI 的硬件不存在一个芯片适用所有场景的说法,计算需求巨大,每一点效率都意味着花费大量的时间、精力和成本。如果没有适当的加速硬件来满足你的 AI 需求,对 AI 进行实验和发现的能力将受到限制。