Appearance
原文:第 2 章 说明:忠实翻译原网页内容,并补入与经典文献、业界系统的对照。术语首次出现给出英文锚点。
第 2 章 CPU 基础、GPU 的兴起与吞吐计算的逻辑
本章在体系中的位置
第 1 章提出了问题,但留了一段悬而未决的推理。1.5 节说「频率缩放结束了、并行上位了」,1.6 节说「GPU 与 AI 工作负载结构对齐」——可是 CPU 到底为什么到头了?GPU 的吞吐逻辑又是从哪儿来的?本章就从体系结构视角补齐这段论证:功耗墙与访存墙如何一步步把 CPU 逼到墙角,又如何塑造出 GPU 这套截然不同的设计哲学。
它在课程里的位置正好承上启下:第 1 章建立「跨层优化 + 物理约束」的问题框架;本章用 CPU 的极限和 GPU 的诞生把「硬件是工作负载驱动的」这句话讲透;第 3 章再钻进 GPU 内部(SM、warp、访存层级、张量核),本章的吞吐逻辑就是它的引论。在 AI Infra 的坐标里,这一章解释的是所有上层工具(CUDA、编译器、分布式框架)共同面对的那个物理现实:单线程延迟不再是目标函数,整机吞吐才是。
第 1 章论证过:机器学习系统本质上是跨层优化问题,机会大多在算法层,但只有被系统软件和硬件高效实现,机会才算落地;现代工作负载由大规模张量运算主导,而通信、存储、数据搬移越来越决定端到端性能。这个框架逼出一个更具体的问题:既然学习系统最终要跑在真实的机器上,那这些机器到底是什么样的、为什么被设计成这个样子、又为什么重心从 CPU 移向了 GPU?
本章不罗列架构事实,而是重建推理逻辑。CPU 不是偶然变得重要的,GPU 也不是仅仅因为「核多」就取代了 CPU。每种架构都是一个特定优化问题的答案。
- CPU 是数十年从「不规则、延迟敏感、控制繁重」的程序里榨取性能的产物。
- GPU 是另一条轨迹的产物:工作负载规律性强、数据并行度极大、目标不是单线程延迟而是总吞吐量。
理解现代机器学习系统,两者都得懂。因此本章的叙述分两个阶段。第一个阶段把 CPU 当作一台高度优化的「延迟机器」来审视:性能如何分解、流水线如何提高利用率、分支预测与乱序执行如何找回指令级并行、编译器如何暴露向量级结构、局部性如何组织访存层级。第二个阶段说明:这些机制再精巧,在张量主导的工作负载上终究会撞墙。那时架构目标改变——不再从一条复杂线程里榨取性能,而是重新设计系统,让它并行扛住海量简单运算。GPU 的世界由此展开。
还有一个贯穿全章的主题。架构不只是硬件,它是工作负载、硬件组织与软件抽象三者之间的契合。这一点在讲到统一着色器架构和 CUDA 时变得关键。GPU 史上那个决定性的转折,不是芯片变大了,而是一台面向吞吐的机器终于拿到足够强大的编程模型,从此逃出图形这个小众领域,成为现代 AI 的计算底座。
2.1 再谈机器学习系统:为什么架构重要
先把语境说清楚。 钻进 CPU 和 GPU 之前,用本课程的语言把系统语境重述一遍。机器学习系统不是「硬件上的深度学习」的同义词,也不能约化成编译器工程、分布式系统或加速器设计的单独一门。它是跨垂直整合栈(算法、系统软件、运行时、编译器、架构、网络、存储)设计、实现、运行智能系统的学科。这些层不是彼此孤立的宇宙,而是耦合的设计变量。
这种耦合重要,因为最大的优化机会往往不在新手预期的地方。改工作负载的算法结构,收益轻松上一个数量级,有时两个;系统软件优化通常带来两三倍;硬件架构每代的增益往往更小。这不意味着硬件不重要,而是说:硬件和软件划定了「算法思想能变得有实用价值」的可行域。 一个数学上优雅、却违反执行底座约束的想法,纸上看起来很颠覆,部署时可能颗粒无收。
讲座用一个简单例子说明这点:稀疏性(sparsity)。从算法看,剪枝(pruning)美妙极了——如果模型去掉一大半参数还能保住精度,你自然会期待立刻加速。但硬件不会抽象地加速「零」,它加速的是规整结构。如果稀疏是无结构的,访存变得不规则、数据布局变得别扭,底层执行引擎可能几乎一无所获。反过来,当稀疏性顺从硬件友好的模式——结构化块稀疏(structured block sparsity)、或 2:4 稀疏这种受限格式——硬件真的能利用它。
记住这句话
优化是垂直的。 算法只在底层知道如何兑现它时,才能把纸面上的节省变成现实。
这正是架构在一门机器学习系统课程里重要的原因。目标不是记住 CPU 或 GPU 的框图,而是理解工作负载结构如何向下传导进机器组织。矩阵乘法是个漂亮的例子。 维稠密矩阵的算术量以 增长,通信表面积却增长得慢得多,大致 。这意味着问题越大,越有机会把工作负载推向「计算密集」的执行——这正是稠密线性代数一直是高性能系统黄金目标的原因。但这个机会只在架构能足够高效地供数时才兑现,于是我们又立刻回到层级、局部性、带宽和延迟。
讲座还有一个容易被忽略的战略要点:架构是一个预测问题。 设计处理器不是只看今天的工作负载。一颗芯片从概念到部署常要两到三年(第 1 章提过同样的时间差);等它上市,它必须适配的是未来那个时刻的工作负载。所以架构总是在「应用如何演化」的巨大不确定性下建造的。这也是本课程反复强调系统研究者必须理解学习工作负载本身的原因之一:没有对未来算法需求的强模型,架构选择就沦为猜测。
所以 CPU 对 GPU 的问题不只是一次硬件对比,而是「当工作负载的重心移动时,计算机系统如何响应」的研究。一旦机器学习被大型规整的张量运算主导,一些架构技术变得次要,另一些变得决定性。本章的余下部分就是在仔细讲这个转变。
2.2 性能的优化分解
任何严肃的架构讨论都从性能开始,但性能必须可分析。只说「这个处理器快、那个慢」,你没有设计抓手。经典的 CPU 分解提供抓手:
其中三个因子的语义分别是:
- 指令数(instruction count):程序需要执行的指令总条数;
- CPI(cycles per instruction,每条指令的周期数):平均每条指令消耗的时钟周期数;
- 时钟周期时间(clock cycle time):单个周期的时长。
术语澄清
这里的 CPI 是 cycles per instruction(每条指令的周期数),不是什么「消费者物价指数」。这个误译在中文资料里流传很广,但在体系结构语境里,CPI 就是一个微架构指标——它越低,一条指令流走得越顺。
这个方程不只是要背的公式,是一张概念地图:程序性能至少能通过三种不同方式提升——减少机器必须执行的指令数;减少每条指令的平均周期数;缩短每个周期的时间。不同的架构思想攻击不同的因子。这正是 Hennessy 和 Patterson 在《Computer Architecture: A Quantitative Approach》里确立的量化分析范式:先显式分解,再定位瓶颈项,然后改机器让那一项收缩。
分解防止混乱。 假设一个新微架构特性让指令数略微增加(编译器得多生成一些簿记指令),但通过暴露并行把 CPI 大幅压下来——这仍然可能是净赢。反过来,假设一个设计提高主频,代价是更深流水线、更多停顿、更高的分支误预测惩罚——那么更短周期的收益可能被更高的 CPI 抵消。架构里满是这类权衡,分解给了我们讨论它们的语言。
讲座正是这样组织 CPU 优化的:终端用户看耗时,系统设计师把耗时分解成对应具体机制的因子。流水线主攻周期时间和利用率;分支预测攻击控制相关的停顿,因而压 CPI;乱序执行攻击依赖导致的停顿,因而压 CPI;循环展开这类编译器变换降低有效指令开销、暴露更多独立工作,再次压 CPI;更好的数据局部性减少等内存的时间,间接也改善 CPI。
停下来想想
这个分解本身已经反映了 CPU 的世界观。CPU 根本关心的是「一条控制线程的进度」:指令是中心语义对象,性能被分析成这条线程在机器里走得有多顺。这是通用处理器的正确模型。但本章后面会看到,对吞吐机器来说主导指标变了——在 GPU 或面向张量的场景里,你关心的往往不是单次操作的延迟,而是大批操作被完成的持续速率。目标的改变,最终会迫使一套不同的分解。
CPU 分解仍是正确的起点,因为它教了一个更方法论的东西:好的架构工作很少靠魔法提升性能,而是把显式因式分解里的瓶颈项找出来,改机器让那一项变小。CPU 的架构史,很大程度上就是「在不破坏通用正确性的前提下,一种又一种巧妙手段压低 CPI」的历史。
吞吐时代的分解:MFU
在当代大规模机器学习系统里,评估 GPU 效率最常用的指标之一是模型 FLOPs 利用率(Model FLOPs Utilization,MFU)。它要回答的问题很直接:训练工作负载把硬件的理论计算能力转化成了多少有用的模型计算?
深度学习 GPU 的峰值浮点吞吐极高,主要来自张量核(Tensor Core)这类专门加速矩阵乘法的单元。拿 NVIDIA H100 来说,张量核的峰值 FP16 吞吐比 CUDA 核的 FP32 高一个数量级不止——但跑到那个峰值极难。MFU 于是提供一种结构化的方式,去推理性能丢在了哪里。
概念上,MFU 可以分解成三个相乘的分量,分别对应机器学习系统栈的不同层:
第一个分量,内核/硬件效率,衡量 GEMM 内核把底层计算硬件用得多好。数学上,它是「最优矩阵乘法时间」与「实测矩阵乘法时间」之比:
最优时间假设 GPU 以峰值 FLOP 吞吐完美运行,实际时间反映内核实现的真实表现。比值越接近 1,说明 GEMM 越接近喂饱硬件。几个因素决定它:矩阵形状(纵横比和分块尺寸决定计算如何映射到张量核乘法阵列的固定几何上);数据布局(连续内存布局、正确对齐、避免多余转置能显著提升吞吐);精度选择(FP16、BF16、FP8 影响张量核的利用效率);内核融合(kernel fusion)——把 bias 加法、GELU 激活融进矩阵乘法的尾声(epilogue),减少访存流量和内核启动开销。
第二个分量,算法计算密度,刻画模型的总体计算时间里有多大比例花在矩阵乘法上:
即使 GEMM 内核本身效率极高,如果很大一部分计算落在无法映射到张量核的操作上,整体性能照样受拖累。现代 transformer 里,理想情况下大多数计算应该在 GEMM 里——query/key/value 投影、前馈网络、输出投影都是。但模型还有大量逐元素操作(GELU 激活、层归一化、dropout)、归约和索引操作;注意力机制带来 softmax 归一化和掩蔽。这些操作算术强度(arithmetic intensity)低,利用张量核的效率远不如矩阵乘法。所以,无论靠算法设计还是内核融合,提高 GEMM 里的计算占比都能显著改善 MFU。
第三个分量,系统并行效率,刻画训练步的时间里有多大比例在做有用计算、而不是等通信或同步:
分布式训练里 GPU 频繁跨设备交换数据来同步梯度或参数。all-reduce 这类梯度聚合引入通信开销;同步优化器状态、维护副本间参数一致性带来额外延迟;流水线并行在阶段失衡时产生流水线气泡。系统层面还有 CPU–GPU 同步延迟、内核启动开销、内存带宽限制。互连带宽——GPU 内部的 HBM 带宽、GPU 之间的 NVLink——常常成为大规模训练集群的限速因素。
三个分量乘起来,就是 MFU 的高低。MFU 高,说明从算法设计到内核实现再到分布式基础设施,整条栈与硬件架构对齐得好;MFU 低,说明栈的某处——内核实现、模型结构、分布式通信——存在低效。对构建大规模训练系统的工程师来说,MFU 既是性能指标,也是诊断工具:它把「该优化哪一层」这个问题,变成了一道可以定位的算术题。有一点要诚实说明:这三个因子是概念上的分解,不是互相独立的旋钮——拧动一个常常会连带拉动另一个,做 MFU 分析时要把这层耦合放在心上。MFU 这个指标本身因 Google 的 PaLM 论文而流行,它首次把「实测吞吐除以理论峰值」作为大模型训练的核心效率指标系统性地报告。
从 CPU 的指令三元组到 MFU 的三因子乘积,分解的其实是同一件事:找出瓶颈项,然后让机器朝着它收缩。
2.3 从指令执行到流水线
性能分解完了,下一个问题是操作性的:CPU 到底怎么执行一条指令流?教学答案从经典的五级流水线开始:取指(fetch)、译码与读寄存器(decode and register read)、执行(execute)、访存(memory access)、写回(write-back)。没有哪颗现代 CPU 真这么简单,但这个模型极其有用,因为它揭示了主要思想。
不流水线的浪费。 如果一条指令从头到尾执行完才开始下一条,硬件被严重浪费:取指时,译码硬件闲着;执行时,取指硬件闲着;访存时,ALU 闲着——机器的大部分在大多数时间里等待。这跟工厂里的浪费是同一种:每件产品占据整个装配车间,可任一时刻只有一个工位在干活。
流水线(pipelining)解决这个问题:让不同指令在不同阶段重叠。第一条指令从取指推进到译码后,取指硬件立刻可以开始第二条指令。几个周期后,流水线里同时有若干条指令,各自在不同阶段。单条指令穿过流水线的延迟未必下降多少,但处理器的吞吐提高了——昂贵的硬件阶段现在忙得多。
讲座把这一点说得直白:没有流水线,一条指令在任意时刻只占可用硬件资源的一小部分。流水线说到底是一种利用率策略。 阶段边界上的流水线寄存器还有一个额外好处:隔离了组合逻辑路径,设计者可以缩短每级关键路径、提高允许的时钟频率。换句话说,流水线常常同时改善利用率和周期时间。
但流水线不免费。 多条指令同时活跃,冒险(hazard)就出现:
- 结构冒险(structural hazard):两条指令想用同一个资源;
- 数据冒险(data hazard):一条指令依赖另一条还没产生的值;
- 控制冒险(control hazard):分支还没解析,要取的下一条指令不确定。
流水线的简洁优雅立刻换来一层新复杂性:机器需要机制让重叠保持有效。
这是通用架构模式的第一个例子。 识别闲置硬件,重叠独立工作,再发明机制管理重叠带来的冲突。这个模式之后会在 GPU 上以非常不同的形式重演——大量线程用来隐藏延迟。具体机制不同,底层逻辑相同:用并发让昂贵硬件保持忙碌。
也值得认清流水线的哲学边界:流水线不创造新的算法并行度,它只是重新组织指令流的执行,让独立阶段能同时被占用。机器本质上仍以一条控制线程为中心。要走得更远,你得开始从指令内部和指令之间提取并行度。
2.4 分支预测、乱序执行与超标量设计
流水线提高了利用率,但一旦流水线存在,下一个障碍是:真实程序满是不规则。分支打断指令的顺畅流动;依赖迫使后续操作等前面的结果;内存延迟不总是短或可预测。如果机器一遇到这类不确定性就天真地停顿,流水线的承诺就消失大半。经典 CPU 工具箱在这里登场。
分支预测(branch prediction)对付控制冒险。遇到条件分支,处理器可以等到条件解析——等待保确定性但浪费周期;或者推测哪条路径更可能——推测有犯错风险但保住势头。现代 CPU 压倒性地选推测,因为平均而言,让流水线满载值得偶尔回滚。好的分支预测器于是成了控制流的统计模型:用历史猜未来。某种意义上,CPU 为了喂饱硬件,在执行一个微缩的在线学习问题。
乱序执行(out-of-order execution)对付另一件事。即使程序顺序里有依赖,也不是所有待发指令同时被阻塞:有些靠后的指令操作数已经就绪,可能正瞄着空闲单元。如果机器坚持严格按序执行,这部分潜在并行就浪费了。乱序执行打破这种僵化:CPU 动态追踪依赖,操作数和执行单元就绪就发射,之后按与架构状态一致的顺序退休(retire)结果。这是个了不起的想法:硬件不再只是执行指令,它在替程序做动态调度。
超标量(superscalar)设计建立在同一原理上:如果同一周期有多条独立指令就绪、机器又有多个执行单元,CPU 可以一次发射多条。这进一步推进指令级并行(instruction-level parallelism,ILP),但也抬高了对前端、依赖追踪逻辑、寄存器重命名(register renaming)机制的要求。机器越宽,越难找到足够多的独立指令填满它,配套控制逻辑也越复杂。
讲座把这些机制呈现为同一个根本问题的一族答案:通用指令流里只有有限的可视并行度,处理器必须积极地把它挖掘出来。 实际效果上,CPU 变成了一个包裹在算术单元外面的「推测 + 调度引擎」。这正是高性能 CPU 核如此精密、如此耗能的原因——它们干着海量工作,只是为了让相对少数的执行单元在不规则代码上保持忙碌。
这也解释了 CPU 为什么擅长控制结构复杂的工作负载:程序里有分支、指针追逐、异构操作、中等工作集时,CPU 的动态智能能维持不错的表现,换成更简单的机器早就严重停顿。但本章在一步步建一个对比:在高度规整的工作负载(比如大型矩阵乘法)上,这些动态机制相对于它们的面积和功耗成本,价值下降得很厉害。如果工作负载已经规整、可预测、大规模并行,你也许宁愿把硅片花在算术和带宽上,而不是花在推测逻辑上。
2.5 软件侧:循环展开与向量化
CPU 性能不是硬件单独创造的。编译器和程序员塑造被执行指令流的结构,在现代系统上这种影响举足轻重。讲座特别强调两个想法:循环展开(loop unrolling)和向量化(vectorization)。
考虑一个简单循环:对数组逐元素施加同一算术操作。朴素形式里,每次迭代都包含循环控制指令——递增索引、与边界比较、没完就分支回去。这些指令必要,但不直接贡献算术负载,它们是开销。编译器展开循环后,一次逻辑迭代的循环控制可以管辖多条算术操作。指令流局部变大,但有用工作与簿记的比值提高了。
更重要的是,展开常常给硬件暴露更多独立操作:好几个迭代并排可见时,乱序引擎和超标量调度器更有机会找到可以并行的活。从这个意义上说,编译器变换和微架构机制是搭档:编译器重构代码以揭示并行度,硬件动态利用它。
向量化把这事再推一步。如果同一操作施加到多个数据元素上,与其逐元素发射标量指令,不如发射一条对多个打包值一起操作的向量指令。这就是 SIMD——单指令多数据(single instruction, multiple data)。按 Flynn 对计算机组织的经典分类(SISD / SIMD / MISD / MIMD),这是「单指令流、多数据流」。这不是新想法,却仍是核心,因为大量数值内核正是这种模式。在 CPU 上,只要数据布局友好、操作够规整,向量化能大幅提高算术密度。
停下来想想
讲座的概念点是:CPU 性能改进发生在软硬件接口的两侧。硬件不能凭空制造指令流没暴露的并行工作;软件也不能在硬件弱到用不上它揭示的结构时提取性能。这个主题预示了后来的 GPU 编程——在那里,暴露并行度和局部性的责任变得甚至更明确。
这里还有一层关于「规整性」的微妙教训:计算越规整,软件变换越容易表达它,硬件越容易加速它。机器学习工作负载恰恰常含这种规整性,尤其在稠密张量算子。这也是为什么 CPU 上的向量化是个很好的桥梁概念——它以缩影展示了:当工作负载允许时,架构如何开始偏向数据并行计算。
但 CPU 终究受制于它最初的任务。即使向量化之后,一个 CPU 核仍是延迟敏感、通用的机器:架构在尝试提高算术吞吐,又不放弃灵活性。直到某一天,工作负载变得太规整、太大规模并行,一种更激进的硅片再分配就会变得有吸引力。
2.6 数据局部性与访存层级的逻辑
到目前为止,讨论集中在机器的「处理侧」:指令、发射、推测、调度、算术。但处理器的有效程度取决于它拿数据的能力。实际上,内存不是一块平坦的资源:不同存储层级在容量、带宽、延迟、能耗之间做出不同的权衡。访存层级(memory hierarchy)存在,是因为没有一种存储技术能同时给出寄存器的速度、DRAM 的容量、以及大型系统需要的成本曲线。
让层级运转起来的关键原则是局部性(locality)。程序经常复用最近访问的数据——时间局部性(temporal locality);也经常紧接着访问附近的数据——空间局部性(spatial locality)。这两件事让系统能把一个相对小的工作集放在处理器附近的快存储里,其余数据待在更远的地方。寄存器伺候最内层的热数据;缓存试图抓住近期的活动窗口;主存存放大得多的主体状态;其下还有二级存储。
讲座强调:缓存不是魔法加速器,是预测设备。 缓存只在系统能通过局部性模式正确预测「该把哪些数据留在身边」时才有用。局部性强,缓存让内存看起来比 DRAM 的裸延迟快得多;局部性弱或访问模式不规则,幻象破灭,处理器停顿。
这就是为什么局部性不是边角话题——它是算法和系统之间最深的桥之一。一个有效复用分块的矩阵乘法算法,可能比算术复杂度相同但实现很朴素(naive)的版本好上几倍,仅仅因为它提高了数据复用、减少了在层级间搬数据的昂贵移动。分块(tiling)、阻塞(blocking)、布局变换(layout transformation)因此不是低级的技巧,而是「把工作负载塑造成与内存系统对齐」的本质手段。
访存墙(memory wall)
局部性这件事背后有一道硬约束。Wulf 和 McKee 在 1995 年那篇著名的短文里把它命名为访存墙(memory wall):处理器性能按指数增长,DRAM 访问延迟却只按线性改善,两者之间的鸿沟逐年扩大。三十多年过去,这道墙只是更高了——从片外 DRAM 取一个字的能量可以媲美几百次乘加,DRAM 延迟是几百上千个处理器周期,寄存器只要几个(第 1 章 1.4 节给过同样的量级)。算术管得过来,数据喂不过来,这个失衡是 GPU 设计逻辑的地基。
到这一步,你已经能看到 CPU 内部正在积累一种张力。处理器用缓存、分支预测、乱序执行、推测逻辑让不规则程序跑得好——但每一种机制都花面积、功率和复杂度。如果工作负载更可预测,其中一部分机器本可以省掉;如果访问模式是流式的、并行度充沛,你也许更该靠软件管理的数据组织,而不是硬件预测。这个观察直接指向 GPU 哲学。
讲座把这个对比说得更明:对高度规整的吞吐工作负载,尤其稠密张量算子,可预测性可以高到「软件调度 + 显式分块 + 结构化的数据搬移」优于大而全的通用缓存。这不是说缓存不好,而是工作负载进入了另一种状态:硬件自动化与软件控制之间的平衡点,该往软件侧挪了。
2.7 为什么 CPU 会遇到极限
到这里 CPU 显得异常能干——它也的确能干:流水线指令、预测分支、乱序执行、每周期多发指令、用向量指令、依赖层层叠叠的访存层级。那为什么它不是现代机器学习的理想引擎?
答案不是 CPU 弱,而是 CPU 为不同的目标做了优化。它的设计中心是:在相对少的复杂线程上取得低延迟进展。机器把大量硅片和能耗预算花在「从不规则性里找回并行度」的逻辑上——推测、依赖追踪、重排缓冲(reorder buffer)、发射逻辑、分支处理、缓存机制。当工作负载满是分支、异构、又到不了大规模并行时,这恰是正确选择。但当工作负载变成一片规整的张量运算时,权衡就变了。
大型矩阵乘法是典型例子。一个稠密张量内核往往有极大的数据并行度、相对简单的控制、分块后可预测的访问结构。在这种场景里,你不太在乎从单条指令流上省几个周期,而在乎在大量独立数据元素上维持巨大的算术速率。KPI 从单线程延迟切换到系统吞吐。
讲座把这次切换讲得非常清楚。在 CPU 风格的世界里,性能的判据通常是一条线程跑完指令序列有多快。在吞吐计算(throughput computing)的世界里,关键是大工作负载上系统能完成多少有用操作。一旦这是目标,CPU 的许多最强项就变成相对低效:
- 内核几乎不分叉,为什么把那么多晶体管预算投给激进的分支预测?
- 操作已经组织成规整的数据并行块,为什么要建复杂的乱序机制?
- 明明有几千条线程可用,为什么优化「单线程尽可能快」?
还有一个物理约束。靠频率提升和不断加深的复杂度来提升单线程性能,终会撞上功耗和散热极限;单纯拉高主频的历史性「免费午餐」早已结束(第 1 章 1.5 节详述过功耗墙(power wall))。于是,维持更高的总性能越来越需要并发而不是频率:工作负载必须并行化,架构必须为高效利用这种并行而建造。
这不意味着 CPU 出局。相反,CPU 对编排(orchestration)、控制密集型代码、操作系统、运行时逻辑和许多混合工作负载仍然不可或缺。但它的确解释了:一旦机器学习成为中心应用,CPU 为什么不再是天然的主导引擎。问题不是 CPU 不会做矩阵乘法——它当然会——而是它的微架构预算没有按对这种工作负载最有利的方式分配。GPU 在这里登场。
2.8 GPU 的诞生路径不同
GPU 不应该被理解成「核更多的 CPU」。它从一开始就体现了另一种架构哲学。讲座用一句锐利的对比点出核心:对 GPU 来说,吞吐量要紧,单线程不要紧。 这句话略带挑衅,但抓住了本质。GPU 并非完全无视个别线程,而是拒绝把整台机器押在一条复杂线程的延迟上。
如果工作负载里有海量的相似操作,架构可以在一个方向上简化、在另一个方向上放大。与其把大块硅片花在动态调度、重推测和大控制结构上,不如把硅片花在算术单元、宽数据通路、以及支撑巨量并发线程的能力上。内存延迟于是主要不是靠单线程的推测智能来隐藏,而是靠线程级并行(thread-level parallelism):一组线程在等内存时,另一组跑起来。
这是观点上的深刻转变。CPU 拼命让一条线程看起来快;GPU 假设没有哪条线程特殊,转而保证整台机器保持产出。讲座强调了这种选择的几个后果:
- 高主频不是主要性能途径。 在功耗约束下,让许多更简单的执行单元以中等频率跑,常常优于让少数单元拼命超频。
- 内存管理变得更显式。 GPU 不主要依赖大型、不透明的硬件管理缓存,而是越来越多地把存储层级细节暴露给软件,期待程序员或编译器有意识地组织数据搬移。
- 可预测性成为设计优势。 规整的流式工作负载让系统能更有效地预取、分块、把搬移和计算重叠起来。
讲座还做了一个很有价值的教学对比。如果你从概念上从一个类 CPU 的核出发,拆掉大部分笨重的乱序机制、推测复杂度和大型缓存依赖,剩下的是一台更简单的执行引擎。复制许多这样的引擎,把它们围绕共享前端结构和大规模多线程组织起来,你就开始逼近流式多处理器(streaming multiprocessor,SM)的逻辑。这不是字面上的历史路径,却是破除 GPU 神秘感的强力方式:它不是魔法硬件,它就是优化目标改变后会发生的事。
与此同时,GPU 的出现不纯粹是 AI 的故事。历史上,它是被图形塑造的。这段历史重要,因为图形里早就装满了那种会奖励吞吐型机器的规整大规模并行计算。要理解 GPU 为什么对机器学习「准备好了」,得先看看图形处理当初要求了什么。
2.9 从图形到通用吞吐计算
讲座最有启发性的部分之一,是它借图形来重建 GPU 历史,而不是直接跳到深度学习。这是正确的叙事,因为它说明 GPU 后来在 AI 里的成功不是偶然:这台机器是被一个结构性质相似的早期工作负载锻造出来的。
计算机图形从三维场景开始,以屏幕上的二维图像结束。从一头到另一头,系统以极大规模执行一串几何和着色计算。一个有用的高层分解是:
- 存储在 3D 模型里的对象被变换到你想渲染的场景配置;
- 顶点被组装成图元(primitive),比如三角形;
- 这些图元被光栅化(rasterize)成大量小片段(fragment)或像素;
- 这些片段被着色(shade)和混合(blend),产生最终图像。
即便在这个简化描述里,大规模并行也显而易见:大量顶点可以独立变换,大量三角形可以独立处理,大量片段可以独立着色。
讲座在变换阶段停留得久,因为它揭示了与线性代数的深层连接。要渲染一个对象,你得把它放进世界、放置相机、把场景投影到二维观察平面。用图形术语说,这是 MVP 链:模型变换(model transformation)、视图变换(view transformation)、投影变换(projection transformation)。在对象层面,可以想象成「摆好对象的姿势,移动相机,拍照」。数学上,这些全是坐标变换。
更具体地:3D 对象操作可以分解为缩放、旋转、平移,课程里讲的标准顺序是先缩放、再旋转、后平移。每个变换用一个矩阵表示,在齐次坐标(homogeneous coordinates)下,对每个顶点的操作通过反复的矩阵-向量乘法实现。变换一拼接起来,渲染流水线就已经在做大量结构化的线性代数。这一点极其重要:GPU 后来成为深度学习的主导引擎,不只是因为它可编程,更因为从图形起源起,它就一直被建造成加速这种并行数值负载的机器。NVIDIA 在 1999 年用 GeForce 256 把「GPU」这个词推向大众,那时它的卖点正是硬件化的变换与光照(T&L)——一张卡替你算几何。
这个视角也解释了一个疑问:为什么早期 GPU 流水线是固定功能(fixed-function)的?在最早的图形时代,工作负载被理解得比较好、晶体管预算紧张。既然大量应用执行同一序列的渲染阶段,把这些阶段硬连线(hardwire)就合算。固定功能硬件对一个稳定目标给出极好的效率;灵活性的成本高,在早年还付不起。
如讲座所述,演化是分阶段的。先是硬连线图形加速器;然后是越来越可编程的图形处理器——顶点着色、片段着色这类环节变成由软件控制;最终,凭借统一执行资源和更通用的编程栈,GPU 逃出狭窄的图形流水线,成为通用吞吐引擎。2006 年的 G80(GeForce 8800 GTX)是第一个统一着色器架构,也是第一代支持 CUDA 的硬件。CUDA 一到,同一台擅长变换顶点、着色片段的机器,就可以直接瞄准科学计算、数值线性代数和机器学习。
历史和架构叙事因此严丝合缝地咬合:图形要求大规模并行、规整的数值内核、高算术吞吐;机器学习后来要求同样的东西,只是规模更大。
2.10 可编程性、统一着色器与灵活性的含义
从固定功能图形硬件到可编程 GPU 架构,是计算机架构史上决定性的时刻之一,讲座把这背后的逻辑讲得很透。一个自然的问题是:早期 GPU 为什么不一上来就可编程?毕竟可编程性听起来当然更理想。答案在于灵活性有成本,而早期几代的硬件,工作负载和晶体管预算都不足以支付这笔成本。
两股力量最终改变了这个等式。第一股是应用需求。 图形工作负载越来越复杂,游戏开发者想要更丰富的效果、更复杂的光照、更多样的几何处理、更自由地定义渲染逻辑——固定流水线变得太束缚。第二股是摩尔定律。 晶体管预算增长后,架构付得起更多灵活逻辑,而不必牺牲全部效率。讲座给了一个很实在的说法:一旦晶体管够多,机器就「买得起」可编程性。
但光可编程还不够。GPU 的中间世代常常仍被划分成独立的功能流水线——顶点单元和像素/片段单元。这已经比纯固定功能硬件灵活,却带来一个失衡问题:不同的图形场景对各个阶段的压力比例不同。如果机器有固定数量的顶点处理器和固定数量的片段处理器,一个阶段成为瓶颈时,另一阶段的资源在空转。
统一着色器架构(unified shader architecture)通过把执行资源汇入一个公共底座、按需分配,解决了这个问题。这是重要的一步概念跨越:机器不再把不同着色阶段当作永久独立的硬件孤岛,而是把它们当作映射到更统一执行织体上的不同种类的工作。利用率提高,因为硬件能适应工作负载的阶段构成。更长远地看,架构变得更像通用计算引擎,而不是狭窄的图形电器。
这就是架构中「灵活性」的更深含义。灵活性不只是程序员的方便,它还允许机器在变化的工作负载组合上维持高利用率。当然,每一次增加灵活性都可能损失一些专用效率——这个权衡从不消失。但 GPU 的故事表明:当工作负载家族拓宽到一定程度,可编程灵活性创造出的平台,其总影响力远超一个更窄但略高效的加速器。
这条教训与现代 AI 硬件直接相关。领域专用加速器(domain-specific accelerator)在窄算子集上可以极其高效,但工作负载一变就有过时风险。GPU 持久的强项在于:它保持高效率的同时,还能可编程到足以适应变化。统一着色器正是促成这个平衡的步骤之一。
2.11 CUDA:决定性的软件抽象
如果统一着色器让 GPU 在结构上更通用,那么 CUDA 让这种通用性变得可用。这就是为什么 CUDA 不应被当作边缘的软件层,而应被当作一次核心的架构事件:一台编程模型孱弱的吞吐机器,只能困在小众里;一台编程模型强壮的吞吐机器,能成为平台。
CUDA 同时做了好几件事:
- 它给了程序员一种用内核(kernel)、线程块(thread block)、网格(grid)表达大规模并行工作的方式;
- 它暴露了硬件的层级执行结构,而不必让程序员用原始图形流水线的语言思考;
- 它给了软件区分不同存储层级的词汇,并在需要处鼓励显式管理局部性;
- 它自带生态:编译器、库、调试器、运行时,以及后来海量的示例和社区知识。
讲座恰恰强调这一点:CUDA 是软硬件协同设计(hardware-software co-design)的那一层,它把 NVIDIA 从一家图形公司变成了通用并行计算平台的管理者。 这个框架完美贴合本课程的论点:硬件单独不赢,软件抽象单独不赢,赢的是架构的优势与程序员访问它们的能力之间的对齐。
CUDA 与 GPU 哲学的契合度也说明问题。GPU 希望工作被组织成大量相似线程;CUDA 要求程序员直接暴露这种工作。GPU 依赖结构化执行和感知局部性的组织;CUDA 提供软件描述这些结构的抽象。硬件与软件模型因此深度对齐。
这种对齐解释了 GPU 为什么成为机器学习如此强大的载体:大张量内核天然可以分解成并行的分块(tile)和块;数据搬移常常能用共享内存分块和寄存器阻塞组织;大量线程可以在规整的索引空间上启动。换句话说,深度学习的主导操作,映射到 CUDA 提供的执行模型上,合适得不能再合适。
讲座还点出一个重要的历史不对称。没有好的编程模型,利用高度并行的机器需要英雄式的努力和海量底层代码——这拖慢研究、收窄开发者基础、降低实际影响。CUDA 把门槛降低到「一个足够大的社区能站在上面」,库又把这个效应进一步放大。生态成熟后,GPU 不再只是一个硬件选项,它成了加速数值计算的默认软硬件平台。
这也是本章论证理应在 CUDA 收尾的原因。一旦面向吞吐的架构与有效的编程抽象结合,系统就脱离原生领域,成为未来工作负载的通用底座。机器学习不是作为外星用例到来,而是作为「恰好极度贴合这个平台」的下一代工作负载。
2.12 第 2 章的教训
现在可以用一句话概括本章的思想弧:CPU 和 GPU,是不同工作负载优化问题的不同答案。
CPU 是通用、面向延迟设计的杰作。它流水线化指令执行、预测控制流、动态重排操作、每周期发多条指令、尽量用 SIMD、靠层层缓存让内存看起来很近。所有这些机制都是对一个世界的回应:程序不规则、分支重要、有用的并行有限且难以暴露。所以 CPU 把大量资源花在「动态智能」上。
GPU 在工作负载改变时浮现。如果主导计算是大规模、规整、数据并行的数值处理,设计中心就从单线程的小聪明移向聚合吞吐。原本投在精巧推测和大缓存结构上的硅,可以改投到更多执行资源、高得多的算术密度、以及让巨量线程同时飞行。内存延迟靠并发和结构化执行隐藏,而不是主要靠单线程预测。
图形史不是这个结论的边角料,它是概念的验证。早在深度学习之前,图形工作负载就要求海量的并行变换、光栅化和着色;MVP 流水线已经依赖基于矩阵的变换;缩放、旋转、平移、投影早已是在大量几何元素上重复的数值内核。GPU 通过图形学会了做「大规模规整计算的机器」。深度学习后来继承了这台机器。
从固定功能图形到可编程着色器,到统一着色器,再到 CUDA,这条路说明一个更大的架构原则:专用效率强大,但只强大到「工作负载多样性与未来不确定性要求可编程性」的那个点为止。 架构一旦足够灵活、软件抽象一旦足够可用,GPU 就不只是图形处理器——它成为吞吐平台。
这就是从第 1 章到全书其余部分的真正桥梁。第 1 章确立了机器学习系统是一个垂直整合的优化问题;第 2 章展示了硬件层本身是工作负载驱动的:不理解 GPU 为什么成为主导执行底座,就无法理解现代 AI 基础设施。
下一步因此是钻进 GPU 内部。我们需要理解流式多处理器、SIMT 执行、warp 调度、存储层级、共享内存、寄存器、张量核、分块,以及支撑现代机器学习吞吐的优化逻辑。但这一切讨论,都要在本章完成的概念转变之后才有意义:从以延迟为中心的通用计算,转向以吞吐为中心的并行计算;从固定功能专用,转向可编程平台设计。 这正是第 3 章的事。
延伸阅读
- 量化分析范式与 CPI 分解的标准出处:John L. Hennessy & David A. Patterson, Computer Architecture: A Quantitative Approach(第 6 版,Morgan Kaufmann,2017;后续版本收入「Vector Processors and GPUs」等章节)。
- 访存墙:Wm. A. Wulf & Sally A. McKee, Hitting the Memory Wall: Implications of the Impossible, ACM SIGARCH Computer Architecture News, 1995。
- Flynn 分类法:Michael J. Flynn, Some Computer Organizations and Their Effectiveness, IEEE Transactions on Computers, 1972(SISD/SIMD 等四分类的原始出处)。
- 功耗墙与频率缩放的终结:Mark Horowitz, Computing's Energy Problem (and What We Can Do About It), ISSCC 2014(第 1 章已引);更早可追溯 Shekhar Borkar, Design Challenges of Technology Scaling, IEEE Micro, 1999。
- 吞吐计算与 GPU 架构的官方文档:NVIDIA, CUDA C++ Programming Guide;NVIDIA, NVIDIA A100 Tensor Core GPU Architecture 白皮书(张量核、HBM、NVLink 的硬件背景)。
- 统一着色器与 SIMT 架构:Erik Lindholm 等, NVIDIA Tesla: A Unified Graphics and Computing Architecture, IEEE Micro, 2008(G80 的原始论文,也是第 3 章 SIMT/warp 话题的入口)。
- MFU 指标出处:Aakanksha Chowdhery 等, PaLM: Scaling Language Modeling with Pathways, 2022(首次系统性报告大模型训练的 Model FLOPs Utilization)。
- 图形管线与 MVP 变换的经典教材:James D. Foley 等, Computer Graphics: Principles and Practice(第 3 版,Addison-Wesley, 2014)。