Dwarkesh Podcast

Reiner Pope – 自底向上的芯片设计

Reiner Pope – Chip design from the bottom up

二〇二六年五月二十三日 收听原版播客

Maddx CEO Rainer Pope 从芯片最底层逻辑门(AND、全加器)出发,逐步构建到AI芯片核心的乘累加(MAC)单元,并演示了DADA乘法器如何用p×q个全加器实现面积高效乘法。他指出,寄存器文件到ALU的数据移动成本(MUX)远超计算本身,这促使NVIDIA在Volta代引入Tensor Core(脉动阵列),将矩阵乘法固化到硬件中,使计算量随位宽二次方增长而通信量仅线性增长。Pope还对比了FPGA与ASIC:FPGA通过LUT(查找表)和多路选择器实现可编程逻辑,但面积效率低一个数量级;CPU因缓存和分支预测器导致延迟不确定,而TPU采用暂存器实现确定性延迟。最后,他比较了GPU与TPU的顶层架构:GPU由大量SM(流式多处理器)组成,每个SM含小规模Tensor Core,数据移动带宽高;TPU则采用大粒度矩阵单元(MXU)夹向量单元,能更好摊薄寄存器成本,但带宽受限。

A

我再次请到 Rainer Pope,他是 Maddx 的 CEO,这是一家新的人工智能芯片公司。上次我们聊了数据中心内部的情况。现在我想了解 AI 芯片内部发生了什么。芯片到底是如何工作的?顺便说一句,我是 Maddx 的天使投资人,所以希望你们设计了一款好芯片。另外,如果你在音频平台上收听,最好换到能看到黑板讲解的平台,比如 YouTube 或 Spotify。

B

我先从芯片设计中最小的基本单元讲起,然后逐步构建到整体,比如一个实际的生产芯片包含哪些组件。芯片最底层的基本元件是逻辑门,比如 AND 或 NOT 这样非常简单的门。这些门通过导线连接,这些导线在芯片上必须以金属走线的形式物理布局。AI 芯片要计算的主要功能是矩阵乘法。而其中最基本的原语是乘累加,也就是成对数字的乘法。我们手动演示一下这个计算过程,然后推断出对应的电路。最简单的方式是用一个 4 位数乘以另一个 4 位数来做乘累加。实际上最清晰的原始操作就是乘累加:先乘这两个数,得到它们的乘积,然后再加上一个 8 位数。

A

我能问个澄清性问题吗?为什么这是计算机内部计算的自然原语?

B

有几个原因。这样效率更高一些,但对 AI 芯片来说,自然的原因在于矩阵乘法中的操作。简单来说,矩阵乘法是什么?它是一个三重循环:对 i、j、k 循环,输出 i, k 加上等于输入 i, j 乘以另一个输入 j, k。所以矩阵乘法的每一步都在做乘累加。

A

有道理。

B

另一个观察是,累加步骤的精度通常比乘法步骤高。这可能特指 AI 芯片:你乘的是低精度数,但累加时误差会快速累积,所以这里需要更高精度。这就是为什么我们选择 4 位乘法和 8 位加法。

A

我确认一下理解。有两种理解方式:一是结果值会比输入大;二是如果是浮点数,可能——这部分对我来说不太直观,但原理可能相同。

B

确实是相同原理。另一个独立原理是,当你累加这个数时,你是在加一大堆数,所以会有很多舍入误差累积;而乘法链中只有一次乘法,所以乘法中累积的舍入误差不多。

A

为什么你要加一大堆数?不就是两个数吗?

B

我是说,加法是重复进行的,j 重复很多次。啊,我明白了。

A

所以是的,任何误差都会累积。明白了。

B

有道理。那我们如何手动执行这个计算?作为人类,我们可能会把它分成两步,但也可以用长乘法一次完成。先做乘法部分:我们将这个 4 位数乘以另一个 4 位数的每一位。写出来:1001 乘以这一位,得到这个数本身;然后左移一位,乘以 0,得到全零;再左移一位,乘以这一位,得到 1001;最后一位,又得到全零。这样我们得到一组需要相加的项。在求和的同时,我们也可以直接加上累加项。所以这是总和,是一个 5 项求和。首先,要得到这些中间步骤,我们需要哪些逻辑门?我们需要生成所有这 16 个部分积。如何生成其中一个部分积?以这个数字 1 为例,它是 1。如何通过这个数乘以这里的 1 得到它?实际上可以用一个 AND 门生成:当这一位和这一位都是 1 时,结果才是 1;只要有一个是 0,1 乘 1 或 0 乘任何数都是 0。所以生成所有这些部分积,我们用了 16 个 AND 门。一般情况下,如果做 p 位乘 q 位乘法,就需要 p 乘以 q 个 AND 门。

A

没错。

B

最后,我把它们加起来。实际上,大部分工作发生在求和阶段。我来描述一下这里用的另一种逻辑门。AND 几乎是芯片上最简单的逻辑门,也是最小的。另一个极端是,你通常会用到的最大逻辑门叫全加器。从软件角度看,你可能会认为全加器是加 32 位数,但这里它只是把 3 个单比特数加在一起。你可以把它想象成加 0、1 和 1。当我把它们加起来时,结果可以是 0、1、2 或 3。用二进制表示只需要 2 位。所以它输入有 3 位,输出有 2 位,比如结果是 1 和 0,因为二进制中 2 是 10。这也叫 3 到 2 压缩器,因为它把 3 位输入压缩成 2 位输出。两个输入是 x 和 y 值,再加上一个进位输入。抱歉,三个输入都是同一比特位置的位,比如同一列中的三个位。然后两个输出,我在这里画成垂直和水平方向,以匹配这里的垂直和水平布局,表示同一列的东西在同一比特位置,而相邻列的东西,比如这是进位输出,这是和。

A

所以如果全加器的输入是 101,输出就是 10;如果是 111,输出是 11;如果是 000,输出是 00;如果是 010,输出还是 01。

B

所以本质上就是数出有多少个东西,然后用二进制表示出来。这个电路其实能捕捉到我们人类在做列求和时自然采取的方式。我可以展示一下用全加器求和的一次迭代过程。我这里用的求和方式对人类来说可能有点不自然。人类通常会按列求和,然后记住进位。但这里我们不记住进位,而是直接把它写出来。在这个例子中,我们从最右边一列向左进行。在最右边一列,我们把1和1相加,得到这里的一个0和一个进位1。也就是说,我们对这一对比特用了这个全加器电路,输出了一对比特。现在我们对这一列做同样的事。这一列有1、2、3、4个数。我们可以先取前三个,用全加器处理,得到输出0和0。所以这三个数的和是0、0。这就是把全加器应用到所有这些比特上的完整压缩过程。当我用掉一些比特后,我会把它们划掉,表示已经处理过了。我们再继续往下走一点。到这里,我取这三个数相加,得到1和0。这三个数处理完了。现在我再取1、2,甚至还可以取这三个数,比如现在这样,相加得到1和0,这些数也处理完了。所以,我应该这样看待这个过程:我有一整个需要相加的数字网格。我不断对这里的所有比特应用全加器,每次从一列中移除三个数,然后写出两个数作为输出。这样反复进行,直到最终这里只剩下一个单独的数字。大概就是这样。这个和可能算错了。我这里描述的方法叫做DADA乘法器。这是用全加器实现面积高效乘法器的标准做法。我们来量化一下这个电路的规模,以便对大小有个概念,之后可以拿来比较。我用了多少个全加器?一开始有多少个数?我有16个部分积,也就是所有这些项与所有这些项的乘积,再加上这里要加的8项。所以一开始有24个比特。最终输出产生了8个比特。每一步我划掉3个数,写出2个数作为结果。所以每次使用全加器就消除一个比特。嗯。那么用了多少个全加器?一定是24减去8,所以这个电路里有16个全加器。一般来说,在通用情况下也是如此。会有p乘以q个全加器。在这个电路里。

A

让我确认一下这个逻辑。输入比特是24,等于P乘以Q加上P再加上Q。

B

没错。

A

输出比特只是P加Q。所以P乘以Q加P加Q减去P加Q等于P乘以Q。

B

对。所以我认为这解释了,或者说至少暗示了,我们为什么选择做乘加运算的第二个原因。第一个原因是这实际上就是矩阵乘法中出现的操作,但第二个原因是它给了我们一个非常简洁的p乘以q,非常简单的代数关系。我们描述了这个完整过程,我这里的每一个原子步骤都变成了一个逻辑门,然后导线连接在一起。比如当我有这三个输入,求和产生这两个输出时,如果我把这个映射到物理器件上,会有一条导线把这三个东西连接到一个逻辑门上,产生这个输出。好的,这就是AI芯片内部不同比特宽度的主要基本单元。我们将从这里开始,构建出如何用它来运行你可能需要的所有其他操作。

A

现在问这个问题可能不是时候,但每当英伟达报告说这个芯片能做X次FP4或一半的FP8时,这似乎暗示这些电路是可互换的,而不是有专门的FP4和FP8电路。但按照你这里的映射方式,似乎需要——如果要在逻辑中映射出来,你需要一个专门的FP4乘加单元和一个专门的FP8乘加单元?基本上,你能糊弄过去吗?

B

按照画出来的样子,它们实际上并不是特别可互换。这其实是设计芯片时你必须做的主要选择之一:我该有多少FP4,多少FP8?有时我会从客户需求的角度来考虑。另一种角度是,考虑功耗预算,在FP4和FP8之间平衡功耗预算。

A

但那么当他们报告这些数字,恰好FP4是FP8的两倍时,他们只是恰好选择了给所有浮点类型分配等效的芯片面积。结果就变成了——

B

为什么比例正好是2倍?对,没错。所以部分原因是,当然这不完全等于芯片面积。实际上还有一个数据移动的原因,我们可能稍后在看数据如何进出存储器时会再谈到。从软件层面来看,有一个非常方便的地方:我可以把两个4比特数打包到与一个8比特数相同的存储空间里。所以当我把它们存储到存储器或类似设备时,芯片内部总线的尺寸设计恰好让这个工作得很顺畅。

A

实际上,仔细想想,不只是2倍。听起来它占用的面积是二次方的。

B

确实是二次方的。对。

A

随着比特长度增加。所以这就是为什么更低的精度比你想象中更有利。

B

这是一个非常重要的原因。实际上,英伟达做了一个改变。历史上,直到B100或B200,每次比特精度翻倍,浮点运算次数也翻倍。这个比例恰好是——因为你说的原因,由于这种二次方缩放,这个比例其实有点不对。你应该得到比想象中更大的加速。英伟达的产品规格在B3-100及之后已经开始承认这一点,FP4比FP8快3倍。

A

但按理说应该是4倍。

B

是的。我这里展示的是整数乘法的最简单情况。当你处理浮点数时,就像在 FP4 和 FP8 中那样,还有另一个项,即指数,它会让计算变得更复杂。那么,我们从中已经能看到什么呢?我觉得,你提出的一个重要观察是,存在一种与位宽成二次方关系的缩放,这非常有效,也是低精度算术在神经网络中如此成功的主要原因。但我们现在要做的另一件事是,比较乘法本身占用的面积与它周围所有电路的面积。所以,我们会稍微回溯一下时间,看看在 Tensor Core 出现之前 GPU 是如何工作的,实际上这与 CPU 的工作方式相同。也就是说,我们要把这个乘加单元放在哪里?一般来说,我会描述一个 CUDA 核心或 CPU。你会有一个寄存器文件,存储若干条目。可能像 8 个条目,这里假设是 4 位数字,但通常是 32 位数字之类的。这是 CUDA 核心内部,我会有一个一定深度的寄存器文件。然后,我有一个乘加电路,即乘法和累加电路。它会从这个寄存器文件中取 3 个任意寄存器,执行乘加操作,然后写回寄存器文件。它可能会写回这个寄存器,但可以从这个、这个和另一个随机寄存器读取。所以,它需要 3 个这样的输入。这就是许多处理器的核心数据路径。大多数处理器看起来都是这样:你有一组寄存器,然后有一组逻辑单元或 ALU。我们要分析数据从寄存器文件到 ALU 再返回的移动成本。最终,会有某个电路来决定,我不一定总是选择这个寄存器,我可以在任何时间点选择任何寄存器。所以,第一个问题是,我如何构建这个电路?我要找的电路是一个 MUX(多路选择器)。在这种情况下,它有 8 个输入,每个来自寄存器文件的一个条目,还有一个输出,实际产生这个结果。那么,这个电路的成本是多少?我们只能用 AND 和 OR 门来构建它。我们怎么做呢?用最简单的方法。我们形成一个掩码,比如当我们要读取第三个条目时,我们将每个条目与 1 或 0 进行 AND 操作,取决于是否要读取它。然后,我们将所有结果 OR 在一起。

A

好的。为了确保我理解基础,MUX 的作用就是选择。

B

就是选择。

A

只是选择一个输入。

B

是的。——对软件来说不可见。就像你说,我要输入 3 号,这里就有一个 mux。那么,这个 mux 的成本是多少?一个 n 输入、操作 p 位的 mux。我有 n 行,这里是 8 行,每行是 p 位宽。我必须对每一位进行 AND 操作,所以得到 n 乘以 p 个 AND 门。每个输入我都要判断,是否要掩码掉它?然后,我把所有结果 OR 在一起。所以,会有 n 减 1 乘以 p 个 OR 门,这意味着我有所有这些不同的东西,几乎全是零,但我需要把它们从 8 个选项压缩成 1 个选项。每一步,我需要将一行 OR 到现有行中。

A

明白了。是的。其实有点好笑,你不会从硬件层面去思考。你只是觉得,哦,我就选元素 3,而像这样简单的事情本身就是一个相当复杂的电路。是的。

B

我的意思是,这是所有隐藏数据移动成本的第一步。是的。所以,我们要比较一下,我必须支付这个成本,这里有一个 MUX。实际上,对于乘加操作的三个输入,我每个都有两个额外的副本。所以,我有这个成本,比如这里有 3 乘以 n 乘以 p 个 AND 门,而在我关心的实际电路中,只有 p 乘以 q 个门。如果我们代入实际数字,比如 n 是 8,我仅在数据移动上就有 24 乘以 P 个门,而如果 Q 是 4,在加法器、乘法加法器中只有 4 乘以 P 个门。

A

抱歉,3 是从哪里来的?这里有 3 个不同的输入。

B

明白了。好的。所以,情况是,我在这里暗示的是,所有这些工作,其规模与寄存器文件的大小成正比,而这是一个非常小的寄存器文件,所有这些工作,仅仅是将数据从寄存器文件移动到逻辑单元,就比逻辑单元本身昂贵得多。

A

在最近的 ClusterMax 报告中,Semi Analysis 对近 100 个不同的 GPU 云进行了排名。Crusoe 是仅有的 5 个达到黄金级别的云之一。Semi Analysis 发现,像 Crusoe 这样的黄金级别提供商,其总拥有成本比银级别提供商低 5% 到 15%,即使它们的 GPU 定价相同。这说得通,因为总拥有成本受一系列不同因素的影响,这些因素不一定体现在标价上,但 Crusoe 已经优化了。比如,你检测故障的能力有多强,以及更换故障节点的速度有多快。例如,Crusoe 是最早采用 NV Sentinel 的云之一,这是 NVIDIA 自己的 GPU 监控和自我修复软件,用于提高 GPU 的正常运行时间、利用率和可靠性。这让 Crusoe 能够利用 NVIDIA 从所有不同集群和部署中学到的关于芯片故障原因的知识,从而更早地发现故障。一旦识别出故障,Crusoe 可以在不到 10 分钟内换入一个健康的节点。因为他们不运行裸金属,Crusoe 不必花时间安装操作系统或配置驱动程序。他们只需在一个已经运行且预先验证的主机上启动一个新的虚拟机。如果你想了解更多关于这个或其他 Crusoe 达到黄金级别的原因,请访问 crusoe.ai/dwarkesh。看看一个 mux 的样子可能会有所帮助,比如一个 2 位或 4 位的 mux。

B

是的,很好。我们取一些输入。我们做一个 2 路 mux。所以,我们有 2 个不同的数字。我们有这 2 个输入,然后有一个选择器。它决定,要么选这个,要么选另一个。这是一种 one-hot 编码。这是我们开始的地方。然后,我们想要产生的输出,比如,我们关注这个情况。这是实际输入。我们只想产生这个作为结果。所以,非常繁琐地,我们将这个位与所有这些进行 AND 操作。这会产生将这个位与这一行进行 AND 操作。同样,我们将这个位与这一行进行 AND 操作,产生全零。所以,这里有 4 个 AND 门。这里有 4 个 AND 门。最后,我们只需将这两个 OR 在一起,这给出 1。我们将这两个 OR 在一起,这给出 1。我们将这两个 OR 在一起,给出 0。我们将这两个 OR 在一起,给出 1。所以,这是 4 个 OR 门。这实际上看起来有点像加法。我们在这里做了完全相同的 AND 操作集,我们把这些东西都加在一起,但然后,我们没有用这些全梯电路来压缩,而是用 OR 门做了一个非常简单的压缩。

A

但是,这看起来不像 n 乘以 p 啊。

B

所以,是的,这是针对 n 等于 2 个输入的情况。很好。在一般情况下,我们会有 n 行,每行有 p 位。这样我们就得到了 n 乘以 p 个与门。我在这里描述的电路,几乎所有的成本,大约 7/8 的成本都花在读写寄存器文件上,而只有很小一部分成本在逻辑单元本身。这就是需要解决的问题。这基本上就是 NVIDIA GPU 在 Volta 代之前的状态。CUDA 核心内部就是这种结构。而正是这类问题促使了 Tensor Core 的引入,它更通用的叫法是脉动阵列。所以,如果我们思考如何解决这个问题,我们几乎把所有的电路面积都花在了我们并不真正关心、对软件程序员来说也是隐藏的东西上。而我们真正关心的东西,却只占了很小一部分面积。那么,目标就是让这个部分变大,同时保持这个部分大小不变。所以,演进的思路是,在这个阶段,我们已经把这一行(乘法-累加)固化到了硬件中。而脉动阵列的想法是,向上跳两级循环,把整个这个循环都固化到硬件中。这样,如果我们有一个粒度更大的固定功能逻辑块,也许我们在输入输出上付出的代价就会小得多。

A

听起来你的意思是,如果你在矩阵乘法循环中向上走一步,就可以把平衡更多地偏向计算而非通信。没错。

B

这里我们要利用两个效应。一个是,在每次访问寄存器文件之前,我们可以做更多的事情。另一个是,实际上,在这个循环的某些部分,我们可以利用某些东西保持不变的特点。让我们从视觉上看一下这个矩阵乘法。这部分循环实际上对应的是矩阵向量乘法。我们取一个矩阵,乘以一个向量。怎么做呢?我们把每一列乘以向量,然后求和。所以我们是沿着列方向求和。比如,0 和 3 乘以 3 和 7,然后求和;1 和 2 乘以 3 和 7,然后求和。所以矩阵中的每一个元素都对应一个乘法-累加操作。我们画出这 4 个乘法-累加器。

A

为了确认我理解为什么是 4 个乘法-累加器:如果输出向量中每一列对应的条目是一个点积,那么在这个例子中,它会是两次乘法,然后这两次乘法的结果相加。所以你在做累加。是的。

B

加法部分,实际上每个点积只有一次加法,但我们喜欢从零开始。

A

但这也包括了初始化为零。是的。是的。

B

所以我们的目标是,让计算量呈二次方增长。我们现在的计算量是之前的 x 乘以 y 倍。但我们希望通信量只增长 x 倍。这就是意图,这样我们就能获得 y 倍的优势。我们已经布置好了乘法。我们需要引入一个大小为 2 的向量。这已经符合我们按列的目标。没问题。然而,我们需要管理这个矩阵的通信,它超出了我们的 x 预算。思路是,在 AI 场景中,这个矩阵实际上会在很长一段时间内保持不变。所以,与其从外部引入它——我们这里有一个寄存器文件——我们不想让从这个寄存器文件出来的数据量太大。这个量我们希望大致是 x 的量级。我们不想每个周期都从寄存器文件把整个矩阵搬进来,因为那样代价太高,从寄存器文件出来的连线成本太高。相反,我们的关键技巧是,这个矩阵可以本地存储在脉动阵列中。我们会把数字 0、1、2、3 存储在称为寄存器的门电路中,物理上保存这些数字。然后我们会反复使用这些数字,处理大量不同的向量。

A

所以这里的优化是,矩阵乘法的本质决定了你可以把这个平方的、二次方的东西直接放在逻辑发生的地方,这比你要不断换入换出的输入多了一个维度。没错。我的意思是,这就是矩阵乘法的本质:你做了大量乘法才能得到一个输出值。比如,点积就是多次乘法的结果。所以这种优化意味着,在得到一个输出值之前,你可以塞进大量的乘法操作。

B

没错。没错。是的。为了完整地展示具体的样子,我把这里的 3 和 2 交换了一下。就像这样,0 和 3 会乘以 3 和 7。我们会沿着列方向形成点积。所以,我们要以某种方式把 3 和 7 喂进来。它们会参与这个乘法,也会参与那个乘法。同样,3 也会喂到这里和这里。然后我们沿着列方向求和,从列的顶部开始喂入零,从底部得到结果。从视觉上看,我们有一个沿着矩阵列执行的点积,这正好映射到脉动阵列的空间布局上。所以这是一个垂直求和的点积,这是另一个垂直求和的点积。那么,需要进出寄存器文件的数据是什么呢?输出端有 X 量的数据出来,输入端也有 X 量的数据进来。所以,至少对于输入和输出向量来说,我们实现了目标:进出寄存器文件的数据量只有 X。但这留下了一个问题:我说权重矩阵是本地存储在脉动阵列中的。它最初是怎么进去的呢?在某个时刻,你需要启动芯片并填充这些数据。那么数据从哪来?技巧就是,我们非常缓慢地把它灌进去。最简单的策略是,我们运行一个菊花链:把一个数字喂到这里,下一个时钟周期它就会移到脉动阵列的下一个条目。我们可以对每一列并行执行这个操作。这样,数据也会从这里进来,这又会增加大约 x 单位的带宽。

A

你能把刚才那句话再说一遍吗?

B

所以我们大概知道,我们只会偶尔把数字加载到矩阵中。因此我们想设计一种结构,使得穿过脉动阵列(systolic array)边界的连线数量——就像这里的边界——被限制在 x 量级,而不是 x 乘以 y。一个特别简单的策略是,我们把一个数字加载到脉动阵列的顶行。这可以在一个时钟周期内完成。然后,在接下来的 y 个连续时钟周期里,我们每次都加载顶行,同时把其他所有行向下移动一行。这样,需要从昂贵的寄存器文件(register file)引出的连线就只与 x 成正比,而不是 x²。我明白了。

A

好的。这里涉及两个通信方面的问题:通信时间和通信带宽。是的。你说既然我们只加载一次,那就最大化——最小化带宽。没错。因为带宽等于芯片面积。我们只需通过较窄的通道慢慢加载,因为我们会把这个值保留一段时间。确实如此。有意思。上次我们讨论跨芯片推理时,高层优化目标是增加每单位内存带宽(即每单位通信)的计算量。而这里,我们也在试图增加相对于从寄存器到逻辑传输信息的实际乘法或加法运算量。所以两种情况下,你都在最大化计算相对于通信的比例。

B

是的,这个原则贯穿整个技术栈。这里接近底层,靠近门电路。甚至还有更接近门电路的版本,比如你选择使用的数值格式的精度。我们看到了同样的效应。无论是 ALU 的精度,还是矩阵的规模,都存在一种平方-立方律或平方与线性项的权衡。非常有趣。这个单元是下一个更大的单元。我们有乘法电路,然后在其之上是一个相当大的脉动阵列。我画的是 2x2,但在早期的 TPU 中,这个电路被描述为 128x128。这个电路最终成为实现矩阵乘法最高效的已知机制。

A

我明白了。我们讨论了最大化计算相对于通信的明显必要性。那么,有哪些不那么明显的权衡会让你夜不能寐,纠结于该选 X 还是选 Y?而且答案并不显而易见。

B

是的。我认为芯片设计中的大多数决策都是尺寸决策。在我们目前画出的范围内,所有 AI 芯片都包含这个电路:一个脉动阵列,以及附近提供输入输出的寄存器文件。即使在这个范围内,你需要解决的两个尺寸问题是:脉动阵列该做多大?寄存器文件该做多大?而脉动阵列的尺寸权衡——实际上这两个问题是耦合的——可以这样理解:我设定一个预算,决定芯片面积中多少百分比用于数据移动。比如我规定数据移动占 10%,脉动阵列占 90%。然后我就可以确定寄存器文件的大小。更大的寄存器文件更灵活,能带来更高的应用级性能,但会挤占脉动阵列的面积。

A

有道理。我最近举办了一场征文比赛,请人们就我认为的 AI 领域一些最大未解问题撰写文章。投稿截止日期是上周,所以我用 Cursor 创建了几个不同的界面来帮助我审阅稿件。一个界面将投稿匿名化并隐藏不必要的信息,让我能按问题分组回复、添加备注并记录评分。另一个界面帮助我审阅那些同时希望被我招聘为研究员职位的参赛者。该界面将参赛者的论文与简历和个人网站并排显示,让我能一目了然。Cursor 非常擅长帮助这些模型查看和改进它们的界面。我看到它在内置浏览器中渲染这些界面、截图、点击各个部分并不断迭代。现在,Cursor 是我完成大部分工作的地方。无论是阅读和可视化大量研究论文,还是编写界面来审阅申请,或是为黑板讲座制作闪卡,Cursor 都能让 AI 轻松查看我正在看的内容,帮助我理解并与我协作。所以无论你在做什么,都应该在 Cursor 中完成。访问 cursor.com/dwarkesh。芯片的时钟周期从何而来?什么决定了它的值?芯片的时钟周期到底是什么?

B

首先,值得指出的是,芯片是极其并行的,对吧?一块芯片上有 1000 亿个晶体管。在高度并行的情况下,一个关键需求是同步不同的并行单元。在软件中,通常使用像互斥锁(mutex)这样非常昂贵的同步方法。一个线程完成工作后,会获取存储在内存中的某个锁,然后通知另一个线程它已完成。而在芯片上,我们采用截然不同的方法:大约每纳秒一次,芯片上的所有电路都会暂停片刻,然后同步所有部分——实际上就是每纳秒左右同步一次。这就是时钟周期。整个芯片通常一次性、步调一致地进入下一个操作。在电路中,这通常这样表示:时钟通过寄存器(我们之前画过的存储设备)来调节。可以这样理解:我有一些存储单元,存储一个比特(0 或 1)。然后我有一团逻辑电路,可能是脉动阵列或乘法器之类的。它会产生一些输出。我的输入会进入这团逻辑电路。最终,后面会有一个输出寄存器来写入结果。有一个全局时钟信号驱动所有这些寄存器。它规定:在时钟触发的某个瞬间,该时刻出现在这根导线上的任何值,都会被存储进去。挑战在于,我希望时钟速度尽可能快,因为如果我能跑到 2 GHz,每秒就能比 1 GHz 多完成一倍的操作。但这意味着我对这团逻辑电路的延迟非常敏感,因为任何在此进行的计算都必须在下一个时钟周期到来之前完成。因此,芯片优化的一个主要方向就是尽可能缩短这里的延迟。

A

有意思。那有没有这种情况——因为这里的约束似乎是,如果你加入太多逻辑,就可能错过时钟周期。没错。但如果你加得不够,又会浪费潜在的计算能力。有没有一种情况,你会赌一把,认为某个计算能完成?还是说,要么它在时钟周期内完成,要么就不行?

B

在标准芯片设计中,你会留出余量,使得——我的意思是,虽然存在概率,但这个概率在多个标准差之外,远到实际上可以认为这是一个可靠的部件,它总能满足时钟要求。当然也有一些奇怪的例外,比如跨时钟域时,从一个时钟过渡到另一个时钟,这时你确实需要考虑这个概率。但有趣的是,在主路径上,你只需留出余量,比如让信号提前25%的时钟周期到达,这样它几乎不可能——

A

在时钟同步的地方,也就是寄存器那里。这不是芯片设计师能决定的。这更像是某种产物:嘿,我想要某个逻辑序列,然后你用来把Verilog转换成发往台积电的东西的软件,它会自动决定:嘿,为了让这个工作,你得在这里、这里和这里放寄存器,确保没有一步太长,导致整个芯片的时钟周期被拖慢。

B

没错,这实际上是芯片设计工作中很大的一部分——插入寄存器。这个过程是手动和自动结合的。举个简单的例子来说明你能做什么:你可以把这个逻辑拆成两半。也就是说,与其只有一个逻辑云,不如用两个更小的逻辑云,它们做同样的事,但中间用一个寄存器隔开。对,像这样输入。如果你从中间拆分,就能达到两倍的时钟频率。这很棒,性能翻倍,代价是多了这个寄存器,也就是多了一些存储空间。

A

那么退一步说,为什么整个芯片需要同步?想象一下玩《异星工厂》之类的游戏,没有全局时钟周期,事情做完就完了。铁在传送带上,你想拿就拿。

B

对,用这个类比来说,你需要留意的是,如果我有两条不同的路径通过某些逻辑,比如这里要做计算F,然后这里做计算G,它们最终要在某处汇合做计算H。对。这里会有制造差异。有些芯片上F会慢一点,有些芯片上G会慢一点。所以如果有个信号在这里传播,而F和G的结果要在H处汇合,可能出问题的是F提前到达,遇到了G的前一个值或后一个值。

A

而H需要知道什么时候开始。没错。就像,下一次迭代什么时候开始?这就解释了为什么在同一工艺节点、同一台积电技术下制造的芯片,时钟周期可能不同。比如,两颗3纳米芯片,时钟周期可能不同,取决于它们能否优化确保没有一条关键路径太长,拖慢整个芯片的时钟周期。

B

对。我刚才展示的优化,叫做流水线寄存器插入。我们在中间插入了流水线寄存器。这是时钟速度和面积之间的纯粹权衡。这是简单的情况。还有更难的:我画了一个逻辑流水线,但其他情况下,你可能有一个计算会反馈到自身。比如,它运行某个函数f,然后像这样写回自身。例如,这可能是加法,你每个时钟周期都要加一个数。所以这个小电路本质上就是累加每个时钟周期输入的数字。挑战在于,如果这个加法耗时太长,我能怎么办?如果我试图在中间插入一个流水线寄存器,比如这里,就会改变计算的结果。它不会形成所有输入值的累加和,而是变成两个不同的累加和:偶数的累加和和奇数的累加和。这种逻辑中存在循环的约束,所有芯片都有,这是最难处理的问题,也决定了时钟周期。

A

我不明白为什么这会是个问题,甚至不确定在那里放个寄存器意味着什么,因为它是一种原子操作,对吧?

B

对,但加法其实不是原子的。正如你刚才演示的,做一次求和需要很多工作。所以你可以把早期的工作做完,在中间放个寄存器,再做后期的工作。好,明白了。

A

对。那么这取决于台积电提供的PDK,它规定了芯片上可用的逻辑原语。由他们确保没有原语比工艺节点目标时钟周期更长。但除此之外,还有什么优化?你不能直接说:嘿,这是台积电的所有原语,然后在原语之间按需添加寄存器,直到达到你想要的时钟周期吗?

B

作为逻辑设计师,芯片架构师设定时钟周期。举个例子,台积电提供的原语大致相当于与门或全加器。它们很大程度上取决于电压、频率和你选择的库。但通常,一个时钟周期内可以串行放大约10到20或30个这样的原语。这些原语非常快,大约10皮秒。所以作为逻辑设计师,原则上,如果你只是让寄存器和与门这样循环,你可以得到极快的时钟速度,比如超过4、5、6吉赫兹。但如果你看看这个简单电路占用的面积,这大概是一个门等效大小,面积单位是1,而寄存器大概是8个面积单位。所以这再次说明,几乎所有的成本都花在了同步或通信上,而不是实际逻辑上。这就属于做得过火了。你让时钟速度变得非常快,但代价是几乎所有面积都花在了流水线寄存器上。有意思。

A

所以你暗示的是,你可以有非常快的时钟速度,但实际完成的工作并不多。对,对,对。所以你可以有低延迟,但低带宽,或者说低吞吐量。

B

是的,实际上这会损害你的吞吐量,因为芯片的吞吐量可以看作是每个时钟周期完成的工作量(这取决于面积效率)乘以每秒的时钟周期数。

A

这其实和我们上次讨论的批次大小问题非常相似。如果批次大小很小,那么任何一个用户都能很快收到下一个 token,但在一小时内处理的总 token 数会比原本可能达到的要低。

B

没错,正是这样。如果你提高时钟频率,并行度就会降低。

A

语言模型开始与最优秀的人类预测者竞争。我与两位 Jane Street 的高管 Ron Minsky 和 Dan Pontecorvo 坐下来聊了聊,并问道:在某个时间点,AI 会不会直接取代 Jane Street 所做的一切?

C

有一个世界是我们必须认真对待的,那就是我们将构建大型语言模型或其他 AI 系统,它们严格来说比地球上所有人类都更聪明,在所有认知任务上都更有能力。而交易本身,在我看来,有点像 AGI 完全体,类似于 NP 完全问题。因为归根结底,交易涉及判断事物的价值,这意味着要对未来做出预测。

A

Jane Street 并没有押注 AI 会失败。他们刚刚签下了一笔 60 亿美元的计算资源交易。但 Ron 的观点是,优势的边界在不断移动。

C

我从未像今天这样迫切地需要招聘更多的工程师和交易员。你知道,总有一些我们尚不知道如何自动化的困难部分。而正是这些部分,最终成为了竞争优势所在。

A

你可以在 JaneStreet.com/thorkesh 找到这些职位空缺,并观看完整访谈。好的,我记得之前和 Jane Street 的一位 FPGA 工程师 Clark 聊过,他帮我为我们的上一次访谈做了准备。他解释了为什么他们使用 FPGA。我猜想,对于高频交易来说,吞吐量不如延迟重要。因此,以确定性方式对时钟周期进行非常精确的控制,是最重要的事情。也许我们可以聊聊,为什么不能用 ASIC 来实现这一点,或者为什么 FPGA 更适合——比如,为什么在高频交易中要用 FPGA 来实现确定性的时钟周期。

B

是的,首先,我们来考虑一下 FPGA 与 ASIC 的业务场景。FPGA 和 ASIC 使用的基本上是相同的概念模型:我有一系列由 AND、OR、XOR 等非常小的基本单元构建的门电路,它们通过固定的时钟周期连接在一起,并通过在固定时钟周期内运行的导线相连。所以,任何能在 FPGA 上表达的东西,也都能在 ASIC 上表达,而且 ASIC 的成本大约低一个数量级,能效也更好。但代价是,第一块 FPGA 的成本是 1 万美元,而制造第一块 ASIC 的成本是 3000 万美元,因为它需要完整的流片。因此,FPGA 的业务场景是:我需要一种具有确定性延迟、快速运行时间和高并行度的东西,但我会非常频繁地更改它——比如每个月更改一次。这样我就不想每次都支付流片的巨额费用。那么,FPGA 实际上是如何实现的呢?它有点像在固定的硬件上模拟 ASIC 的编程模型。具体是怎么工作的呢?它的基础是我们刚才提到的两个组件:作为存储设备的寄存器,以及称为 LUT(查找表)的组件,它提供了所有的门电路。然后,我们还有第三个组件:一大群这样的寄存器和 LUT。所有这些组件都可用,并通过一大组多路选择器(mux)连接起来。在每个组件前面,都有一个这样的多路选择器,它从其他地方选择一个输入,从所有这些不同的东西中进行选择。有很多不同的选项输入到所有这些组件中。这实际上允许的是,当我编程 FPGA 时,我可以指定:我将使用所有这些组件,并在其上叠加一个特定的布线,比如通过这个锁存器,然后输入到那个锁存器,再到这个寄存器,再输入到那个锁存器,等等。我用橙色画出的部分就是 FPGA 的含义——现场可编程门阵列。橙色代表在现场被编程的部分,而白色代表为了让设备能够工作而必须预先存在的所有导线。

A

"在现场被编程"是什么意思?

B

现场编程。也就是说,设备已经部署在数据中心里,它已经在现场了,然后你可以来对它进行编程。

A

不是像电场那样的"场",而是像"在现场"那样的"场"。好的。那么,如果我看这个现场编程是如何从第一个查找表输出并进入第二个查找表的,它是怎么做到的?怎么实现的?

B

你是说,实现这一点的导线在哪里?是的。我画这些的时候有点偷懒了。这里的每个设备前面都有一个多路选择器,它可以从所有可用的附近电路中进行选择。是的。所以 FPGA 的实际配置就是多路选择器的控制。比如,在这个多路选择器中,我们有数据输入,然后有用于选择的控制信号。是的。每个多路选择器旁边都有一个小的存储设备,它告诉这个多路选择器应该从哪个输入源获取数据。明白了。所以编程就是配置每一个这样的多路选择器。

A

这说得通。那查找表内部发生了什么?

B

是的,查找表的目的——它也会有一些控制输入来告诉它该做什么——是能够可配置地扮演 AND 门、OR 门、XOR 门或任何其他不同门电路的角色。有很多方法可以实现这一点。在传统的 FPGA 中,做法是:它支持——一个查找表会有 4 位输入和 1 位输出。从 4 位到 1 位的函数有多少种?有 16 种不同的函数。所以你可以把它列成一个表格,就像 16 个不同的数字。你有一个包含 0、1、1、1、0、0、1 等 16 个条目的表格。它的工作原理是:这个表格存储在一个蓝色的配置位中。然后它将这 4 位输入视为二进制数,查找表格中对应的行,并输出该位。这本质上就是查找表的真值表视图。

A

好的,所以查找表——如果你想想 AND 门、OR 门、NOR 门、XOR 门——这些都是以——

B

这些都是 2 输入函数。是的。有时我们会有更复杂的,比如 3 输入函数就是 3 路 XOR。对。或者 4 路 XOR。在这种情况下,有多少种?

A

这取决于它有多大。

B

LUT 的典型大小是 4 输入,这只是一个计算与通信之间的权衡点。如果输入太少,你就需要使用更多的 LUT。是的。

A

如果太多的话。但基本上,查找表就像一个真值表。没错,就是真值表。有了真值表,你可以编程实现任何你想要的逻辑门。是的。那么,它是不是就是一个查找表?就把它想象成一个可编程的逻辑门。

B

没错。所以,这里你可以看到,为什么经验法则说FPGA比ASIC贵一个数量级,原因就在于要计算这个查找表内部有多少个逻辑门。是的。我们可以把这个查找表看作是一个多路选择器。它是一个多路选择器,需要在16个不同的值之间进行选择。所以它是一个有n个16选项、p个1比特的多路选择器。我们之前看到,这个电路的成本大约是n乘以p个逻辑门。所以,它大约需要n乘以p个与门和16个或门。

A

这个电路指的是多路选择器。

B

是的,没错。就是多路选择器。

A

多路选择器是核心。就是进入查找表的多路选择器。

B

所以查找表本身,你可以把它看作是一个大的多路选择器,从所有16行中选择一个输出。是的。我明白了。那就是查找表。

A

但你这里画的,好像有一个多路选择器,然后是一个查找表。

B

实际上层层都是多路选择器。我的意思是,这里面还有第二个多路选择器。

A

这个多路选择器就是那个多路选择器。明白了。然后另一个多路选择器只是说明它来自这堆逻辑门中的哪个位置。

B

对。

A

然后第二个多路选择器说,好了,你现在有一个值,但这个值仍然是4比特的。

B

是的。所以我已经从这堆数据中选出了4个比特。然后我用这4个比特来选择我要使用查找表中的哪个条目。

A

对。好的。为了确认一下,假设在第一个多路选择器中,有8个附近的寄存器,你从8个附近的寄存器中提取输入。那么总共就有32个比特输入。然后从中输出4个比特。这4个比特进入第二个多路选择器,也就是查找表内部的那个。

B

实际上,我会说,在这个例子中,这些寄存器是单比特寄存器。所以如果有8个附近的寄存器和查找表,那么我总共有8个比特输入。我从8个中选出4个不同的值。所以实际上有4个不同的多路选择器,每个对应一个输入比特,每个都从8个中选1个。那这8个来自哪里?来自附近的寄存器和其他查找表。每个寄存器是1比特?是的。

A

那么我想,AMD或任何制造这些FPGA的公司,仍然必须对哪些寄存器应该连接到哪些寄存器做出规定,然后你可以编程实现实际的逻辑门,但他们必须预先布好连接,也就是通信拓扑,对吧?

B

是的。所以你在局部粒度上有一定的灵活性。有一个附近的邻域你可以从中选择,但更粗略地,更远距离的连接,他们就会对此做出规定。

A

对。是的。那为什么它会慢10倍?

B

如果你看看构建这个查找表的成本,大约是32个逻辑门。然后它可以给我相当于什么?一个有趣的事情是,我可以实现一个4路与门。那就像是我用了32个查找表逻辑门来实现一个4路与门。4路与门是什么?我会做与、与,然后再与。所以,这是一个我可以在ASIC中直接用3个与门实现的电路,但用查找表,我也可以实现,但它需要32个逻辑门而不是3个。

A

所以开销实际上来自查找表,查找表中的多路选择器,因为描述一个真值表有一种更简洁的方式,而不是列出每一个可能的输入组合,那就是直接写出逻辑门。

B

是的,直接放置多晶硅和导线等等。

A

有意思。他向我强调的一个重要点是,他们更喜欢FPGA而不是CPU的原因是他们能得到确定的时钟周期。他们知道数据包何时进入和离开。为什么在CPU中不能保证这一点?

B

实际上,你也可以设计一个具有确定延迟的CPU。事实上,许多AI芯片内部的处理器也具有确定延迟。Groq已经宣传过这一点。TPU的核心也是如此。挑战在于同时实现确定延迟和高速度。那么,延迟的不确定性来自哪里?延迟的不确定性来自CPU中的特定设计选择。实际上,可以移除这些设计选择,制造一个具有确定延迟的CPU。但这些CPU在市场上不太有吸引力,所以人们不再制造它们了。但事实上,从某种意义上说,确定延迟可能是一个更简单的设计起点。然后一些芯片设计者添加了一些东西,使其变得不确定。举一个具体的例子,可能最重要的例子是CPU上的缓存。在CPU中,这是CPU芯片本身。然后旁边有一个内存,这是旁边的DDR内存。然后你在这里面有一个缓存系统,它记住最近对DDR的访问并存储它们。所以,当我运行CPU指令时,每次我有一条访问内存的指令,它首先检查缓存,数据是否存储在缓存中?如果没有,它就去DDR获取。

A

是的。

B

这是一个巨大的优化。缓存比DDR快大约两个数量级。如果你从不使用缓存,基本上所有程序都会慢100倍。所以缓存的存在对于CPU以合理速度运行是绝对必要的。但你是否能命中缓存,取决于CPU的周围环境,比如其他程序在运行什么,最近运行了什么,缓存系统内部的随机数生成器在做什么。所以,这是CPU运行时间中一个很大的不确定性来源。是的。这就是CPU的内存系统。你可以做的不同之处在于,与其让硬件说,我要读取内存然后决定,硬件决定数据是否来自缓存,你可以把这个决定嵌入到软件中。所以一种不同的设计哲学是,你可以在例如TPU中看到这一点,TPU使用的是,我会画同样的图,但我会称之为暂存器。所以主要区别是,这会是TPU,然后这里是HBM而不是DDR,但它仍然是片外内存。而不是让软件先访问内存然后硬件决定,你有一些指令去这里。这是一种指令,然后是完全不同种类的指令去HBM。是的。所以,这种风格通常被称为暂存器而不是缓存。关键区别在于,你有一种指令说读取或写入暂存器,和一种完全不同的指令说读取或写入HBM。

A

所以暂存器就是缓存?

B

是的,这个东西就是暂存器。

A

先澄清一下。退一步说,人们常说计算机采用的是所谓的冯·诺依曼架构,也就是信息的串行处理。也许是因为我们一直在讨论并行加速器,但我就是觉得FPGA并非超级并行。实际上,各种AI加速器、TPU都是超级并行的,甚至CPU如果你考虑它拥有的所有核心,也是超级并行的。那么,现代硬件在多大程度上真的还是冯·诺依曼架构?用这个来描述现代硬件公平吗?

B

我认为用来描述CPU是公平的,单就并行度而言。比如在CPU上,你能获得的并行度大约是100个核心乘以16路向量单元,也就是大约1000路并行度。

A

有个问题是——CPU所用的芯片,如果线程数更少,从晶体管电压或开关切换的角度看,是不是真的只有一条控制流,芯片上只有一小部分区域在切换电压?或者说,如果核心很少,你实际上是如何占用CPU芯片面积的?

B

如果核心这么少,整个芯片面积都用在核心上了吗?是的。核心本身更大、更复杂。所以,我觉得应该拿一个CPU核心(占芯片面积的1/100)与一个LUT(查找表)比较,LUT只有16个门。这就很清楚为什么FPGA里的LUT比CPU里的核心多得多。但至于为什么CUDA核心比CPU核心多,这就要看CPU和GPU之间的根本区别了。在CPU内部,面积最大的部分是缓存,真正用于ALU(算术逻辑单元)的面积很少,大部分是寄存器文件而非逻辑单元。而这两者在GPU中都有对应,所以这不是主要区别。但GPU中没有对应的是分支预测器。CPU中有一大片区域全是预测器,用来判断下一个分支何时出现以及分支目标在哪里。去掉这些,同时让寄存器文件更紧凑,正是GPU性能提升的关键。

A

分支预测器的目的是什么?是同时执行两个分支吗?

B

问题是,当有一系列指令时,如果遇到一个分支指令,处理这个指令的实际步骤需要很长时间,大约5纳秒。也就是说,从检测到分支、评估布尔条件、更新程序计数器到新目标、再从指令内存读取,整个过程可能需要5纳秒才能完成。而我希望时钟速度远高于5纳秒所允许的200 MHz,比如1或2 GHz。所以在分支被评估的同时,我需要运行其他指令。我希望能继续执行分支之后的指令,但如果分支实际被跳转,我就得知道应该跳转到目标地址执行那些指令,而不是继续执行后续指令。因此,分支预测器的目的就是在你到达这个指令之前,提前5个周期预测分支会发生。

A

那么,如果对比大脑的工作方式和这里描述的芯片,高层次的区别可能是:在这些加速器中可以做结构化稀疏,从而节省原本要用于门的面积;而大脑中是非结构化稀疏,任何神经元都可以连接到任何其他神经元,不需要列对齐之类。另外,大脑中内存和计算是共址的。我想可以说,在某种程度上内存和计算是共址的——

B

这正是内存和计算共址的概念。没错。

A

对。所以这可能不是主要区别。另一个主要区别可能是大脑的时钟周期比计算机慢得多。部分原因是为了节省能量,因为时钟周期越快,信号稳定并识别晶体管状态所需的电压就越大。没错。不知道你对大脑和这些芯片的工作方式有没有其他高层次见解。

B

我们先说时钟速度。芯片的时钟速度很高,因为这能提高吞吐量。当GPU运行某个工作负载时,它处理的是批次大小为1000的数据,而大脑不是这样,只有一个“我”。所以可以想象,如果把GPU从千兆赫降到兆赫运行,它可能就更接近大脑的情况。但硅的工作方式决定了,这并不会带来1000倍的能效优势。最终的结果是,你让电路运行一次直到稳定,然后闲置很长时间。闲置时消耗的能量不多,因为大部分能量消耗在比特从0到1再到0的切换上。我们来谈谈这种电路的能量消耗。存储一个比特的方式是,你在芯片某个电容中沉积了电荷。当比特为1时电容充电,变为0时放电。这个充放电循环就是能量消耗的来源。这被称为动态或开关功耗,是芯片大部分能量消耗的来源。还有一些来自绝缘体不完美的漏电,但我们可以忽略。大部分能量消耗实际上来自0到1再到0的切换。所以,如果你把芯片运行得慢很多,比如每1000个时钟周期才触发一次,那么切换次数会减少1000倍,能量消耗也大约减少1000倍,但这并不是能效上的显著优势。

A

好的,你已经从高层次描述了TPU的工作原理。那么GPU和TPU在高层次上有什么区别?

B

对,我觉得首先在顶层组织原则上就有不同。然后核心内部也有差异,但我们先看外部,也就是顶层结构。我们拿 GPU 和 TPU 来对比,看看它们顶层模块长什么样。如果把每个芯片看作整体,GPU 的布局主要是大量几乎相同的单元,也就是 SM(流式多处理器)。中间有一块 L2 内存,下面还有更多 SM。整体上是一个相当规整的核心网格。再看 TPU,相比之下,它的逻辑单元粒度要大得多。你会看到数量不多但规模很大的矩阵单元,也就是那些大的脉动阵列。中间是向量单元,下面又是矩阵单元。所以 TPU 芯片整体就是矩阵单元中间夹着一个向量单元。你可以把它缩小成一个很小的单元,包含一个小矩阵单元和一个小向量单元,这差不多就是一个 SM 的规模。所以从顶层视角看,GPU 相当于在整个芯片上铺满了大量微小的 TPU。

A

有意思。所以你的意思是,流式多处理器里的 Tensor Core 和 MXU(矩阵乘法单元)是类似的?

B

对,非常相似。我明白了。

A

那么如果结构更松散,用一堆小 TPU 就很合理。但如果只是做大规模矩阵乘法,为什么不干脆避免每个 SM 单独配备寄存器、warp 调度器等带来的开销?为什么不直接做一个大单元,把这些成本摊到整个芯片上?

B

我觉得这体现在你能把规模做多大上。我们之前看到过这个主题,尤其是脉动阵列,更大的脉动阵列能更好地摊薄寄存器文件的成本。这种设计允许你使用更大的脉动阵列,而 GPU 的设计则限制你只能使用小单元。不过这里有个权衡。由于这种粗粒度的分离,你需要把大量数据从向量单元搬到矩阵单元,只能通过两条参数线来传输。而在 GPU 中,向量单元遍布各处,数据可以通过多条线路传输——这条、那条、再那条……所以 GPU 中向量单元和矩阵单元之间的数据传输带宽实际上比 TPU 高得多,因为 GPU 不需要把所有数据挤在两条线上,而是可以通过 16 条甚至更多线路来传输。

A

而且传输距离也可能更短。

B

这也是一个节省,能省能耗。如果数据完全在单个 SM 内部流动,传输距离就小得多。但一旦需要跨 SM 操作,就会变得更复杂、更昂贵。

A

你不必评论,但可以推测 Maddox 可能会尝试做的是:采用类似 GPU 的稀疏结构,让脉动阵列被 SRAM 包围,同时去掉 SM 中那些支持 CUDA 架构但占用大量空间的东西。

B

对,我们公开讨论过一种叫可拆分脉动阵列的东西,某种程度上你可以把它理解为既能当大脉动阵列用,也能当小脉动阵列用。

A

酷。好吧,我觉得这是个不错的收尾。Reiner,非常感谢。谢谢,Rakesh。

译自 Dwarkesh Podcast · 录于 二〇二六年五月二十三日