专题二 · TPU 与 GPU L200

这份讲义怎么用

这是老师的草稿,不是给学员的材料。学员看 课件 L200,你看这一份。
课程作者 Chris Yang · Google Cloud AI Infra 架构师。

🎯这一讲要留下什么 讲完之后学员脑子里应该剩的那一句话。 只有一句 —— 讲散了就是没讲到。
🗣讲稿 接近逐字,可以照着念。黄底的是必须说出口的原话, 其余按自己的语感改。
🖥屏幕 这一段该滚到课件的哪里、该指哪张图的哪一格。
可能被问到 真实会被问的问题 + 答法。 答不上来的就写「这个我不确定」 —— 讲义里编一个,现场你会照着念。
⚠️别讲什么 这一讲最容易跑偏的方向。时间就是被这些吃掉的。

舞台指示这几个词,具体是什么意思

下面这几个词在全篇反复出现。它们不是形容词,是有具体做法的。 台上照这个执行,不要凭当时的感觉。

停死念完最后一个数立刻闭嘴,心里默数两拍再开口。 不是「稍微停一下」—— 临场的默认动作是接着讲, 不给自己一个可数的量就一定会滑过去。
念出来屏幕上照原样打出来,嘴里报中文、一行一个数英文原文(报错、指令名)念不出去,硬念台下也接不住。
只念不解释念完必须补一句前向指路: 「先记着,×× 时候会回来用它。」 不交代去处的伏笔不是伏笔,是噪音;而且要说清在哪一节收。
语速放慢把那一句拆成两个短句说,中间断一次。 「放慢」控制不住,断句控制得住。
要真的等等到有人出声或举手为止,不设时间上限; 真没人应,点一个人问。自问自答等于这三十秒白花。
指着指完立刻念出那一格上的字, 别让台下自己找。手势不携带信息,念出来才携带。

还有一条通则:凡是解释「为什么这么讲」的句子 (例如「惊讶感一点不减,这句经得起追问」)——  那是给你自己的判断依据,不要念出来。 念出来会把听众从内容拉到你的方法论上。

⭐ 全课那条线:算子融合

这门课表面在讲硬件差别,真正在讲的是一件事:一个算子该不该跟邻居合成一个 kernel,以及这件事由谁来决定。先记住这张对照 ——  不要另开一段讲融合,在这五处各点一句名字就够了:

在哪这一处怎么点
第 1 节 那条线刚定出来、还没用过的那一刻,给它起名字:想让慢算子变快只有一条路, 把分母做小,而最有效的办法就是让中间结果别落 HBM。约 40 秒
第 2 节 「朴素 vs Flash」就是融合前后FLOP 一个没变,强度差一千倍 —— 这是全课对融合最有力的一次演示,不用额外说明
第 3.6 节 同一个融合,在两边分别怎么落地:块开多大、谁来切
第 5 节 ⭐ 「运行时还是编译期」问的正是融合由谁决定全课在这里收口 —— 前面所有铺垫都是为了这一句
第 9 节 收尾也收在这条线上:不比谁强,只说这个决定权交给了谁

这些时间怎么分

下面这张表是计划,不是实测 ——  每讲过一遍,回到生成脚本里把真实分钟数改回去(表是按那些数生成的, 改一处就够)。

分钟讲什么这一段的落点
0–8开场:两种出身(不再有那张路线图把「处处都不同,但只有一个成因」这个论点立住。它现在只是论点,不是结论
8–18第 1 节 · 定分界线:算力 ÷ 带宽,两边撞在同一个数上第一个「坐直」的时刻:312.9 对 312.5,参数表在这里被拆穿。节末给全课那条线起名字:算子融合,并当场说清它不是让搬运消失、只是换了一层
18–28第 2 节:同一次取数,一边路上 4 个运行时决定,一边 0 个论点第一次变成可看见的东西。也是全课对算子融合最有力的一次演示 —— 同一个注意力,FLOP 一个没变,强度差一千倍
28–70第 3 节 · 上:整颗 → 一个核 → 核里那块地 → 并行层级 → 一条指令吃多大一块这半节只交零件,不交结论。每一层都要能指出「两边差什么、是谁的决定造成的」—— 结论在下半节
70–80⭐⭐ 第 3 节 · 下FlashAttention 两边并排走完一遍 + 回收第 1 节那个峰值巧合全课的高潮就在这 16 分钟里。讲不完别的都能砍,这段不能砍 —— 砍了这门课就退回成参数对比
80–84第 4 节:同样是 64 颗 —— 一边在一个 NVL72 域里(一跳),一边是 4×4×4 环面(最远 6 跳它是第 6 节那组实测的前置 —— 那两个数跑在两种结构不同的网上,而这门课没拆开量过
84–96第 5 节 · 把那个词正式拆开:运行时,还是编译期开场那个论点在这里收口 —— 四次出场合成一件事。换句话说:融合这件事,两边分别交给了谁
96–104第 6 节 · 跑完之后:两个数摆上桌,能不能比。分母看入场券,分子看 MFU / HFU产出不是那个倍数,是「这个数往哪边都有理由」。⭐ MFU 与 HFU 在这里正经讲一遍 —— 它是分子那一侧的口径,也是「边界」那张图第 ③ 行的前提
104–109第 9 节 · 收尾:那到底各自擅长什么回到最开始那句:不比谁强,只说由谁决定

主线全讲 109 分钟(这一版没有可跳的岔路: §4 已经从「岔路」提回主线,它是第 6 节那组实测的前置)。
这不是一小时的课:2026-09-03 拍板不砍内容, 要一个准点的时段就按这个数往上取整要。真只给 60 分钟,用下面那份应急清单。

讲不完的时候先砍哪个

顺序是固定的,临场别现想: ① 第 4 节压成两句(不要整节跳 —— 它是第 6 节那两个数的前置, 但左右两张图各指一眼、把「一跳 vs 六跳」说掉就够,−4); ② 第 6 节只留那张两个数的实测图,边界那张压成一句(−4) —— ⛔ 但 6.2 那段 MFU / HFU 不在可砍范围内,它是分子那一侧的口径, 砍了下面「边界」图的第 ③ 行就没词可讲; ③ 第 3 节前三小节各砍一张图(−4)。
⛔ 第 3.6(FlashAttention 并排走那一段)和第 5 节,任何情况下都不能砍 ——  砍掉它们,这节课就退回成「两块硬件参数对比」, 而那正是开场第一句话说了不做的事。

00

开场:两道热身题 + 两种出身

⏱ 8 分钟
这一讲要留下什么 两边处处都不同,但这些不同全是同一个决定的后果。 那一处是「把一块数据从 HBM 搬进片上,谁来安排」——  一边交给运行时,一边交给编译期。整节课其余部分都是在验证这一句。

讲稿

课件停在首屏,先不往下滚。

好,我们开始。这一讲叫「TPU 与 GPU」,但我先说清楚, 这门课不给你念参数表。参数表恰恰是我们第一个要拆穿的东西。

⭐ 往下滚一点,停在那道黄色的选择题上。 四个选项先别点 —— 点下去答案就出来了。

不过在开始之前,先回一道上一课的题,热热身。 上一课我们拆的是 DeepSeek V3,总参数 671B,每个 token 只激活 37B。 那 MoE 到底省下了什么?

这里一定要真的等。举手、或者让他们喊出来都行, 但要等到有人给出答案再点。 —— 这道题的作用不是考知识,是把房间从「听」切到「答」。 自己念完自己点开,这三十秒就白花了。

答案是「省算力,不省显存」。算的时候只走 37B 那条路,算力是真省了; 可 671B 的权重一个字节都不能少放 ——  你不知道下一个 token 会挑中哪几个专家,所以全都得在显存里待着。

点开答案,指着最后那一段(⭐ 那行)念。

但我真正想让大家听见的不是这个答案,是它背后那件事 ——  显存和算力是两样东西,会各走各的。上一课整整一课都在分开算这两笔账。
而这一课要加的是第三样:带宽。 —— 而且待会儿会看到,这三样里真正决定一个算子快不快的,常常不是前两样。

这句「第三样是带宽」是这一讲和上一讲的接缝, 也是第 1 节的引子。说完就往下走,别在这儿展开 ——  兑换比那个数第 1 节自己会算出来。 ⭐ 再往下滚一点,第二道黄框题。同样,四个选项先别点。

还有一道,这道纯猜。这门课后面会把两颗芯片拆开、一层一层往里看。 在开始之前先猜一下 —— B200 上那个矩阵单元叫 Tensor Core, v7 上那个叫 MXU,干的是同一件事。整颗芯片上,两边各有几个?

这道一定会有人猜错,而且错得很整齐 ——  大多数人会觉得两边一个量级。让他们错,这道题的价值全在这儿。

B200 是 592 个,v7 是 4 个。份数差 148 倍。 —— 但真正该惊讶的是下一句:这两颗一拍能做的乘加总数只差 15%。 同样一份算力,一边切成 592 小块,一边切成 4 大块。

念完就停,不要解释「为什么」。 指着答案区最后那一行念 —— 数字给你了,「为什么」先欠着 这里最容易犯的错是当场展开。 一展开,这道题就从钩子变成了剧透, 第 3 节那二十四分钟就没人有问题了。 —— 要的效果是他们揣着「为什么好几百对个位数」听完前两节。 到第 3 节 3.7 那笔账的时候,会有人自己喊出来。

整节课就两个问题,一条路线。第一个问题,这两块硬件在硬件层面 到底哪儿不一样。第二个问题,我们拿同一个算子,从头到尾跑一遍, 看那些不一样分别在哪一步冒出来。那个算子就是 FlashAttention。

往下滚一屏,停在第 0 节那张图。整张图只有两个框, 不用找别的东西 —— 这一讲要做的就是把这两个框说透,然后立刻往下走。

现在看第一张图。全课只有这一个前提,就这两个框。

左边 GPU,出身是图形处理器。它从第一天起要伺候的, 就是一大堆互不相干的小任务 —— 形状不知道,访问模式不知道, 控制流也不知道。所以它的芯片面积,大半花在「应付各种情况」上。 cache 层次、warp 调度器、海量并发线程,全是为了这个。

右边 TPU,出身是为矩阵乘定做的专用芯片。它从第一天就只服务神经网络。 形状是规则的,访问是可预测的,控制流是静态的。 所以那一整套「应付各种情况」的电路,它一样都不用做。

这里最容易顺嘴说错,而且我自己说错过: 别说「省下来的面积全给了计算单元」。
省下来的既没变成算力,也没变成片上存储 ——  第 3 节下半会拿两张表把这两条都堵死:乘加总量 GPU 反而多 16%, 片上 SRAM GPU 多 73%。

那它省下来的到底是什么?—— 是复杂度本身。 没有 cache、没有调度器、布局在编译期就定死; 同样多的算力,它只切成四大块,而不是五百九十二小块 ——  少了一百四十八套控制电路。

⚠️ 台下多半会接一句「那是不是更省电」这一问有官方答案,而且很硬 —— 但答的时候要带边界。

可以答,念两样就够。第一样是 TPU 初代论文里的原话 ——  它把省掉的东西一口气列完:没有 cache、没有分支预测、没有乱序执行、 没有多线程、没有上下文切换。然后下一句是: 「Minimalism is a virtue of domain-specific processors.」

⭐ 第二样是**一个数**,比什么形容词都管用: 「那颗 die 上,控制逻辑只占百分之二。」
数据缓冲三成七、计算三成、I/O 一成 —— 控制,两个点。 论文紧接着写:这一块在 CPU 和 GPU 上大得多。
然后必须刹车,三条边界一条都不能省:
一,那篇论文比的是 2015 年的 TPU v1 对 K80 ——  那一代省下来确实换成了算力,同样一颗小 die 塞了二十五倍的 MAC。 而到 v7 对 B200,第 3 节下半会数出来 GPU 反而多百分之十六。 同一个机制,十年之间结果反过来了。
二,Ironwood 官方那个每瓦性能翻倍,比的是上一代 TPU,不是 GPU。
三,良率和可靠性 —— 没有出处,不说。 官方讲的是「确定性执行」,那是尾延迟,不是可靠性,别混。
停一拍。下面这两句是这一讲要背下来的。

一个是「不知道你要跑什么,所以处处留一手」; 另一个是「早就知道你要跑什么,所以一手都不留」。

不用滚了,就停在这两个框上说下面这段。

那么这门课要讲的是什么呢。两边处处都不同 ——  但这些不同,全是同一个决定的后果。

哪一个决定?就是把一块数据从 HBM 搬进片上的时候,谁来安排。 GPU 那边有 cache 在运行时替你猜;TPU 那边没有,编译期就排死了。

形状要对齐到 8 乘 128、一条指令吃多大、kernel 谁来写、 连一台机器要付什么代价 —— 全都是从这一处长出来的

这里语速放慢。这句话是整节课的论点, 后面每一节都在给它找证据。说太快,学员会当成一句口号听过去。

开课前还有一句话我要说死:这门课不比谁强。 两种出身各自都是当年的正确决定,代价也各自都还在。 所以整节课你不会听到「谁更好」这种句子, 只会听到「这件事在这一边由谁决定」

根立完了。下面直接进第 1 节 —— 它只做一道除法,很快; 做完那道除法,第 2 节就开始拆硬件

可能被问到

说「只有一个成因」,会不会又太一刀切了?

现象上确实是十几处,说「一个」指的是因果上的一个。 cache、形状对齐、kernel 谁写、互联怎么连 —— 这些都是真的不一样, 但它们不是并列的十几个事实,是同一个决定往下长出来的十几个后果。

这句话在开场是一个论点,不是一个已证的结论。 可以坦白说:「你现在完全可以不信,第 3 节和第 5 节我会分别验证它。」 —— 这样比硬撑更有说服力,而且给了他们一个听下去的理由。

TPU 真的一点 cache 都没有?那 VMEM 是什么?

准确的说法是:TPU 没有「硬件自动管的缓存」那一层。 片上存储是有的,而且不小 —— 但它是软件管理的 scratchpad: 什么时候搬进来、放哪儿、什么时候倒掉,全部由编译器在编译期写死, 运行时没有任何东西会替你做决定。

这个区别正是本课的主线,所以被问到是好事,顺势就能接: 「对,缺的不是容量,是『谁做决定』。」
⚠️ 但别在开场展开讲 VMEM 有多大、分几块 —— 第 3 节有专门的图。

为什么拿 FlashAttention 当主角?换个 GEMM 或者跑一整个模型不行吗?

因为它同时满足两个很难同时满足的条件。

一是单卡就跑得完 —— 不用把卡间通信拽进来, 故事能一条线走到底(这也是第 4 节被画成岔路的原因)。

二是它一次压到四件事:访存、形状、片上容量、kernel 怎么写。 纯 GEMM 太干净,压不到后面三件;跑一整个模型又太杂, 每一步都在换话题,最后什么都没讲透。

第 4 节为什么画成岔路?卡间通信不重要吗?

重要,但它不在这条主线上。判据很硬: 第 4 节的正文里 FlashAttention 出现 0 次(grep 过的)——  它单卡就跑完了,根本撞不到卡间

与其让人读到那儿觉得「怎么突然跑题了」,不如在地图上就标成岔路。 L200 里这一节是折叠的,主线时间不够可以整节跳过, 对第 5 节完全没有影响。

别讲什么

这一讲最容易跑偏的四个方向 ① 别在开场比参数。 算力、带宽、SRAM 那三组数是第 1 节的包袱 ——  「两边几乎一样」这个意外,得等到那条线定出来了才炸得响。开场先说了,第 1 节就白讲了。
② 别讲两家的历史年表。 出身只需要两句话(「不知道你要跑什么」/「早就知道」), 多一句都是在花时间。
③ 别在这儿解释 MXU 是什么、warp 是什么。 第 3 节有八张显微镜图专门拆。 开场做术语扫盲是最贵的一种跑偏 —— 一开口就是十分钟。
④ 别说「谁更好」。 一旦有人把话题带到选型,直接用课件上那句挡回去: 「这门课只回答『这件事由谁决定』。」
节奏提示 这一讲两分钟必须走完,它是根不是内容。 真正让人坐直的时刻在第 1 节末尾(312.9 对 312.5), 早点走到那儿。
如果开场发现台下背景很杂,宁可砍掉出身那两段的细节, 也要把「处处都不同,但全是同一个决定的后果」这句说完 ——  没有那句话,后面每一节都会变成孤立的知识点。
⚠️ 这句话图上没有,它只在你嘴上、和课件那个绿框里 —— 更不能漏。
01

先定那条分界线:两边的胃口一样大

⏱ 10 分钟
这一讲要留下什么 312。从 HBM 搬 1 个字节要花一段时间,那段时间里硬件能做约 312 次 BF16 运算。 这一节不给任何结论,只交一个数 —— 后面每个算子都要拿自己的比值跟它对一眼。
⭐ 而且要在节末说清这条线是干什么用的:判一次算子融合值不值得做。 全课那条线的名字,在这一节出现。

讲稿

滚到第 1 节。这一节只有两张图,节奏要快。

上一段我说了「只有一个成因」。但在比任何东西之前,得先知道这台机器上「算」和「搬」的兑换比是多少。 这一节就干这一件事 —— 把那个数算出来,不下结论

两边的官方规格表都给了两个数:每秒能算多少次,和每秒能搬多少字节。 我们把它们除一下。这个比值的意思很实在 ——  搬一个字节要花多少时间,那段时间里这块硬件能算几次

这句话的方向特别容易讲反,慢一点说不是「算 312 次要配 1 个字节的原料」,是「搬 1 个字节的工夫,机器能干 312 次活」。 所以它是一条最低要求:你的算法每搬一个字节,配套得算够 312 次, 才刚好把算力喂饱;算不够,多出来的算力就在那儿空等。 停在那张两栏对照图(fig1-4)。不要急着往下滚, 让数字在屏幕上待一会儿。

左边 TPU v7,一颗芯片 2,307 TFLOPS,带宽 7.37 TB 每秒 (= 8 × 921.6 GB/s;本课统一用 7.37,别处写过的 7.38 / 7.4 都是同一个数的取整)。 除出来 312.9

右边 GB200,NVL72 里那一颗,2,500 TFLOPS 配 8.0 TB 每秒。 除出来 312.5

这里停死。数字念完不要马上解释, 让台下自己反应过来这两个数是一样的。这是第 1 节唯一的爆点。
具体怎么停(2026-09-10 口述后补):念完「312.5」就闭嘴, 心里默数两拍再开口。「停死」只写了要停、没写停多久, 而临场默认动作是接着往下讲 —— 不写出做法的指令, 到台上就等于没写。(同型教训:外传那条 XLA 报错, 讲义只写「念出来」,可整句英文根本念不出去。)

两家公司,两套架构,两种设计哲学 —— 比值撞在小数点后一位上。 换成 FP8 也一样,625.8 对 625.0。

这里千万别说「相对差万分之三」。 那个第三位是假的 —— GB200 那个「8 TB/s」官方没写口径, 十进制和二进制两种读法本身就差 2.4%
要说的是这一句:「两家、两套架构、两种哲学 ——  算力除以带宽,落在同一个量级上。」 惊讶感一点不减,而且这句经得起追问。
这一句是给你自己的判断依据,不要念出来(2026-09-10 口述时 我把它念出去了)。判据:凡是解释「为什么这么讲」的句子都属于讲义, 不属于讲台 —— 念出来会把听众从内容拉到你的方法论上。
指主线上那条蓝线(「同样 8 颗 24 GB 的 HBM3E, 带宽为什么不一样」)。这一句主动讲,别等人问 —— 它每次都会被问。 一句话答完:「HBM3E」只说了代次,没说档位。
每颗 stack 一千零二十四根数据线,带宽就是这个宽度乘以每根引脚的速率。 而这一代从八点零到九点六 Gbps 都叫 HBM3E ——  v7 跑七点二,GB200 跑七点八上下。差别全在引脚速率。
再补一句最要紧的:用哪一档是买方挑的,按功耗、散热、可靠性挑, 不是芯片能力的上限。
先补一句口头过桥,别硬接(2026-09-10 补): 引出 HBM3E 那问用「说到这儿,有个问题每次都会被问,我主动讲」; 答完转到下面这句用「然后是这一节我最想说的一句话」
原来这两处是两条舞台指示直接相邻,中间没有可念的句子 ——  台上会出现一个突兀的跳转。
然后这一问就把这门课的判据打回到自己头上了。
⭐ 然后把那句最值钱的说出口:「而『这个数是二进制还是十进制』, 正是第 6 节要教的那四句问法之一 —— 它现在打在我自己的招牌数字上。 那四句不是拿来审别人材料的,是先拿来审自己的。」

但接下来这句必须跟着说,不能省「都落在 312」是真的, 「为什么都落在 312」是我的推断,没有任何一家这么解释过。 而且它挑型号 —— 换成 HGX 板上那颗 B200,就是 281.3,差 11%。

所以我们后面每次拿 312 当判据,用的都是那个带限定的版本往外引的时候不能把限定甩掉只留数字。

往下滚一屏,图还没完 —— 下半段有三根长短不一的条。 只念不解释,二十秒。

顺手说一句:刚才那个除法,其实每一层都能做一次。 HBM 是 312,L2 是 119,共享内存是 64。 —— 越靠近计算,这条线越低。

这里不要展开。台下现在还不知道要它干嘛, 解释了也存不住。本节最后讲融合时会回来用它,那时候才有落点。
⚠️ 但要念一句前向指路:「先记着,这一节最后会回来用它。」 (2026-09-10 口述时补的)「只念不解释」如果什么都不交代, 台下会当成一句没头没尾的插话直接丢掉 ——  钩子必须说清在哪儿收,否则它不是钩子,是噪音。 同一条判据在外传那边也踩过:X1 埋的钩子写错了在第几节收。
回到那两个面板 —— 还是同一张图,不用换

比值一样,还有一种可能:分子分母凑巧成了比例也不是。 面板上四个数就摆在那儿:算力 2,307 对 2,500,带宽 7.37 对 8.00 —— 两项都在 8% 以内

这句是这一节真正的收获: 不只是比值一样,分子和分母各自也几乎一样。 凑巧成比例这条退路,堵死了。

顺一句规格表:十行并排看每行都一样重,但值得记的只有一类 —— 一边有、另一边整个没有。TPU 没有硬件自动管的片上缓存那一层, GPU 没有可编程的专用协处理器那一层。缺的不是容量,是「谁做决定」。 —— 那两层具体长什么样,第 3 节有专门的图,这里一个字都不要多说。 完整十行折在本节最后,回查用的手册,现在不展开

滚到「这条线怎么用」那张三栏图(本节第二张图), 先停在第 ① 栏这一段是本节时间的第二个重点,别当收尾一带而过。

线定出来了,但光有 312 没用 —— 它是硬件那一侧的数, 你还得会算算子那一侧的数,两个数才能比。

算子那一侧的数叫算术强度,算法只有一句话: 数它搬几个字节,数它算几次,除一下。

这句是这一节最实用的一句,说慢一点。 它比 312 本身更值钱 —— 312 会随硬件换代变,这个算法不会。 滚到「这条线怎么用」那张三栏图, 先停在第 ① 栏算式就印在图上,你只要念着走。

我们当场量两个。

第一个,向量加。y = a + b,bf16。 读 a 两个字节,读 b 两个字节,写 y 两个字节 —— 一共搬 6 个字节。 中间做了几次运算?一次加法。所以强度是 1 除以 6,0.17

跟 312 比 —— 低了一千八百多倍。

这意味着什么?这行代码在 GB200 上把 8 TB 每秒的带宽全部跑满, 也只有 1.3 TFLOPS —— 那颗芯片峰值是 2,500。 用掉万分之五。算力再翻十倍,它一秒都不会快。

这里可以停一下问:「所以买更贵的卡, 能让这行代码变快吗?」让他们自己答「不能」。 移到第 ② 栏

第二个,矩阵乘。N 乘 N 的方阵,bf16。 乘加次数是 2 N 的三次方,搬进搬出三个矩阵是 6 N 的平方 字节。 约一下 —— 强度等于 N 除以 3

那要多大才够到 312?边长接近一千。

反过来说:边长 256 的矩阵乘,强度只有 85,还在带宽那一侧。 —— 「矩阵乘就是算力受限」是个错觉,它得够大才算数。

这一句一般会有人抬头。它推翻的是一个 几乎所有人都默认成立的常识。 同一张图,移到第 ③ 栏这四十秒是全课那条线的起点,别赶。

到这儿可以给这门课真正的主角起名字了。

刚才那两行算术其实说的是同一件事:一个算子快不快, 取决于它每搬一个字节做多少次运算。 那想让一个慢算子变快,就只有一条路 —— 把分母做小,少搬。

而少搬最有效的办法,是把连着的几个算子合成一个 kernel, 让中间结果根本不落 HBM这就是算子融合。

这句停一下:它一个 FLOP 都没省。 分子分毫不动,变的只有分母 —— 很多人以为融合是「少算了」,不是。

所以融合在这条线上的效果,是把算子往右推 —— 从带宽那一侧,推到算力那一侧。

还是第 ③ 栏,指下半那个黄框(312 → 64 那两个数)。 这一段是补上去的,因为上面那句话会被听岔。

刚才我说「中间结果根本不落 HBM」。这句话很容易被听成 「中间结果消失了」。—— 它没有消失。

它被塞进了片上暂存:GPU 上是那块最多 227 KB 的共享内存, TPU 上是 VMEM。HBM 那一层确实少搬了,可那批数据总得在片上落脚。

这句停一下:搬运没有消失,是换了一层。 刚才那三根条,用处就在这儿。

而片上那一层有它自己的线 —— 刚才念过,是 64,不是 312。 所以融合完要重新对一次账:拿新的强度跟 64 比。

这里必须多说三句,否则上一句自相矛盾: 门槛从 312 降到 64,是降不是升。

那为什么还会撞墙?因为动的不只是门槛,还有分母。 融合把 HBM 那一层的流量转成了片上流量 —— 片上搬的字节数能涨几十倍, 而门槛只降了 5 倍撞墙不是因为门槛变高,是因为分母变大。

台下常会接一句:「那干脆留在寄存器里不就完了?」 —— 大多数时候做不到。一个 SM 的寄存器堆总共 256 KB, 跟共享内存一边大;而且你多占一份,能待命的 warp 就少一批这笔账第 3 节那张 SM 拆解图上有。

1.2 方阵只是特例 —— 强度由最小的那个维度说了算(图 1-8)

滚到 图 1-8。⏱ 这一段 3 分钟, §1 相应地从 10 分钟变成 13 分钟。
这一段是 2026-09-10 现场追问逼出来的,不是原设计。 原话三连:「M×K · K×N 呢?强度应该跟收缩维 K 有关,K 越长越高对吧? 而 head_dim=128 的注意力就严重不够。那 Flash 之后是因为什么变了?」
三问全对,而且它暴露了课件上一条断掉的线: §1 只讲了方阵,§2 直接跳到 Flash,§3.4 又单说收缩维 ——  三处讲的是同一个 128,但从没接起来过。
⚠️ 所以这一段只要三分钟,但不能跳:它是 §1 → §2 → §3.4 的接缝。

刚才那两个例子里的矩阵乘是方阵,三边都是 N,约出来是 N/3。 但真实的矩阵乘是 M×K · K×N那强度跟哪一个有关?

还是同一道除法。算的次数是 2 M N K, 搬的字节是 2 乘以 MK 加 KN 加 MN。约完是一个很干净的形式 ——  强度的倒数,等于一比 M 加一比 N 加一比 K。

「倒数相加」这四个字要单独说一遍,它是这一段的钥匙: 倒数相加,意味着谁最小谁说了算

所以「K 越长强度越高」是对的,而且是单调的。 但它有天花板 —— 图上第 ② 栏那五行就是拿来看这个的: M 和 N 都固定在 512,K 从 64 拉到一百万,强度从 51 涨到 256 就不动了。 上限是 MN 除以 M 加 N,正好 256。

K 救不了 M 或者 N 太小。最狠的一个特例:batch 等于 1 的 decode, M 就是 1,于是强度上限就是 1 —— K 和 N 再大都没用。 这就是 LLM 解码怎么都卡在带宽上的那一行算术。

现在把注意力代进去。QK 转置的两个外维都是序列长 S, 而收缩维就是 head_dim。多数模型 head_dim 是 128, 于是强度约等于 128 ——  低于 v7 那条 312,也低于 v6e 那条 560。

这句要说出口,它推翻一个默认成立的常识「注意力是矩阵乘,当然算力受限」是个错觉 ——  不做融合的话它落在带宽那一侧,而卡住它的正是 head_dim。

那 Flash 改的是什么?方向先给,完整那一笔下一节第一张图会推它一个 FLOP 都没省,改的是分母。 中间那个 S 乘 S 不再落 HBM,过 HBM 的只剩 Q、K、V、O ——  强度变成 S 除以 2。拿 Wan 那个 S 等于 75,600 代进去, 就是 37,800

⭐⭐ 这一段真正的落点在最后这句,别赶
同一个 128,咬了两口。 一口是这里 —— 收缩维只有 128,强度上不去; 另一口在 §3.4 / §3.6 —— MXU 是 256 见方,只喂进 128, 方阵一半是空的
前者问「要不要等数据」,后者问「算的时候算得满不满」。 两件事,同一个数字。
这里有一个口径坑,必须主动说破,否则 §2 一到就穿帮:
图 1-8 说注意力强度 ≈128,可 §2 的图 2-4 写的是 64两个都对,分母不一样 ——  128 是单个矩阵乘孤立看(A/B/C 各过一次); 64 是朴素注意力整体看(中间矩阵 S 和 P 各走一个来回,分母多一倍)。
⭐ 图 1-8 底下那条黄带就是写这个的,指着念一遍就行。 不说破的话,细心的人翻到 §2 会以为其中一张画错了。

好,线定出来了,也会用了,也知道它是干什么用的。 第 1 节的全部产出就一样 —— 一个数,和一个算它的方法。 数会随硬件换代变,方法不会。

⛔ 2026-09-05 起课件上那个「产出就两样」的框已经删了 —— 原来第 ② 条讲「一边有、另一边整个没有才值得记」, 那条判据在 §1 是悬空的(本节根本没推出它),而它在 3.5 / 3.6 用到的地方 上下文里自明,不需要预先定义台上也别补讲,讲了就是把删掉的东西又搬回来。

—— 接下来每一节都在这条线上。第 2 节看融合前后差多少: 同一个注意力,两种写法 FLOP 一模一样,强度差一千倍; 第 3 节看它在两边分别怎么落地; 第 5 节回答那个真正的问题 —— 这件事该由谁来决定。

可能被问到

这个 312 是 BF16 的,跑 FP8 呢?判据要换吗?

要换,换成 625。图上两行都算了:TPU 625.8,GB200 625.0。

道理很简单:算力翻倍,带宽没变,所以比值也翻倍 ——  而且两边都翻倍,所以「撞在一起」这件事在 FP8 上依然成立。

判据要按你实际跑的精度取。拿 BF16 的 312 去判一个 FP8 的算子, 会把本来算力受限的判成带宽受限。

为什么 TPU 那栏写「每 chip」?我看日志里的数对不上。

因为 v7 是一颗 chip 里有两个 device,而框架日志一律按 device 报。 所以看日志换算:per-chip = 日志上的 per-device × 2

这里两边都按「一颗封装」的口径比 —— TPU 一个 chip 对 GB200 一颗 GPU, 这样才是同一个东西在比。口径写在图里那一行小字上,讲的时候可以指一下。

⚠️ 这个 1 比 2 还会影响 batch:per_device_batch_size 里的 「device」在 v7 上是半颗芯片。但这里不要展开,是另一门课的事。

算力除带宽这个数,有正式名字吗?

有 —— 它是 Roofline 模型里屋脊点的横坐标,也叫机器平衡点。 算子那一头对应的量叫算术强度,单位一样,所以能直接比大小。

提一句名字就走,别画 Roofline 图。画那张图要五分钟, 而第 2 节那张访存图讲同一件事讲得更具体。

⭐ SM 有 148 个 —— 共享内存那条 64,是不是把所有 SM 加起来算的近似?

先肯定他 —— 这是全课最会算账的一个问题。 但答案比他以为的更硬:那个数没有加总,它本来就是按一个 SM 算的。 分子 8,192 FLOP、分母 128 字节,都是「每 SM 每周期」

那换成全芯片口径会不会不一样?不会,而且这不是近似,是恒等。 全片算力 = 148 × 每 SM × 时钟,全片共享内存带宽 = 148 × 128 B × 时钟 —— 148 和时钟同时站在分子分母上,直接约掉。 课件底下那行反推校验走的就是这条路:2,500 ÷ 64 = 39 TB/s 全片, 再除以 148 × 128 得到时钟 2.06 GHz ——  这是 GB200 那颗(§1 全节用的都是它)。

这里原来写的是「跟 B200 对得上」,标签错了 (2026-09-10 复核 §3 时查出来)。2.06 GHz 属于 GB200: 2,500 ÷ (606,208 × 2) = 2.062。
HGX 上那颗 B200 是另一组数:峰值约 2,250 TFLOPS、 第三方 boost 1.83 GHz(按 8,192 FLOP/SM 反推约 1.856)——  代进同一条式子得 ≈ 35 TB/s,正是 §3.2c 那一行
所以 §1 的 39 和 §3.2c 的 35 不是打架,是两颗不同的芯片被问到就这么答。反过来,台上千万别说「2.06 跟 B200 对得上」 ——  本课自己的 GPU 显微镜把 B200 钉在 1.83/1.856,会当场被戳穿。

⚠️ 但另外两条线(312、119)确实是全芯片口径。 三条摆在一起能比,靠的正是上面那个约分 —— 这一点值得当场说破, 否则细心的人会一直觉得这张图偷换了口径。

⭐ 这个问题真正的价值,是逼出「比值一样、可达性完全不同」这件事。
HBM 那 8 TB/s 是 148 个 SM 抢的一份共享资源 ——  你一个 SM 拿不满,148 个一起读则人均只剩几十 GB/s。
共享内存那 128 B/周期是每个 SM 自己的 ——  一个 SM 跑是这么多,148 个一起跑还是每人这么多,总量线性放大。
—— 所以融合的好处不只是「门槛从 312 降到 64」, 更是从一份要抢的资源,换到一份自己就有的资源

顺带纠一个方向:门槛低是好事 ——  I* 越小,算子越容易越过它变成算力受限。 那为什么融合过头还会撞墙?不是门槛变高了,是分母变大了: 门槛只降了 5 倍,而融合把 HBM 流量转成片上流量,片上搬的字节数能涨几十倍。

算术强度是算子的属性,还是硬件的属性?

算子的。312 是硬件那一侧 —— 「搬一个字节的工夫,我能算几次」; 算术强度是算子那一侧 —— 「我每搬一个字节,实际算了几次」。 同一个单位,所以能直接比大小。

但有个地方特别容易搞错,而且它正好是下一节的引子: 算术强度不只由数学决定,也由你怎么实现决定。

同一个注意力,朴素写法和 Flash 写法,FLOP 一模一样,强度差一千倍 —— 差别全在搬了多少字节。这就是第 2 节要讲的事, 被问到就顺势预告一句,别在这儿讲完。

⭐ 312 的分子只取了矩阵单元。CUDA Core 和 VPU 就不算了吗?

这是这一节最好的一个问题,一定会有人问 —— 先肯定他。 312 确实是「矩阵那条 roofline」的屋脊点,向量单元没算进去。

先答为什么这么取:本课要判的算子(注意力的两次矩阵乘、MLP 的大矩阵乘) FLOP 几乎全在矩阵单元上,softmax、归一化那些是零头。

再给量级:GB200 那 18,944 个 CUDA Core, 每周期 2 FLOP、约 2.06 GHz,合起来 78 TFLOPS ——  矩阵那边 2,500,差 32.5 倍。TPU 那边结构上一样: 两个 MXU 一拍 262,144 次乘加(每个 MXU 131,072),VPU 一拍 1,024 个元素。
⚠️ 那个时钟是推出来的,讲的时候要说出来 ——  NVIDIA 没公布 GB200 的 boost 频率。

然后承认他是对的:严格讲,判 element-wise 算子该用向量那条 roofline, 屋脊点是 9.6 不是 312。

最后收回来:结论不翻。向量加强度 0.17,比 9.6 还低 58 倍, 照样卡在带宽上。换哪个分母都惨。

然后把它变成一个收获,这才是这个问题真正的价值: 向量单元弱不是疏忽,是两边共同的设计选择 ——  算子一旦落到向量单元上,你输了两次:带宽受限一次,可用算力只剩三十分之一又一次。 这就是为什么必须做算子融合:融合不是为了少算, 是为了别让数据在这条又窄又弱的通路上来回跑。

课件里这一段折在第 1 节末尾,不要主动展开讲,被问到再翻 ——  它是分母之争,对本课的结论不影响。

两家参数为什么会这么像?是互相抄的吗?

这个我不确定,没有任何一家公开解释过。直说就行。

可以给一个标明是推断的说法:两边都在拿 HBM 带宽去配矩阵算力, 用的还是同一代 HBM,比值撞在一起并不奇怪。但这是我倒推的,不是出处。

⚠️ 这里特别容易顺口讲成因果 —— 「两边被同一批负载钉在同一个比值上」 这种话听着很漂亮,但它是从两个数字反推的,而且换个 SKU 就不成立了(281.3)。

别讲什么

这一讲最容易跑偏的五个方向 ① 别展开 Roofline。 被问到就报个名字,不要画屋脊图 ——  五分钟起步,而且第 2 节有更具体的讲法。
② 别把十行表摊开逐行念。 它在课件里是折起来的,就是为了不念。 主线只要一句「值得记的只有一类:一边有、另一边整个没有」,十行是回查用的。
③ 别在 SparseCore、CMEM 上停留。 第 1 节只需要说「各缺对方一层」, 那一层具体是什么,第 3 节有专门的图
④ 别把 312 讲成「TPU 和 GPU 是一样的」。 这一节证明的是 「参数表分不出它们」,不是「它们一样」 —— 这两句差得很远, 而且下一节就要打脸第一句。
⑤ ⭐ 别在这儿讲「怎么做融合」。 这一节只给它起名字, 四十秒 —— 什么算子能合、编译器怎么找融合机会、为什么有时候合不了, 一个字都不要说。这里是全课最容易一脚踩进去讲二十分钟的地方, 因为它确实是最重要的话题。但它的证据在第 2、3、5 节,不在这儿 ——  在这儿讲,就是拿没有证据的结论去换后面三节的时间。
节奏提示 六分钟,两张图 + 两行算术 + 一个名字。这一讲有三个重点,不是一个。
重点一是 312.9 对 312.5 那个停顿 —— 念完留住三十秒不说话, 让台下自己发现。这是全课第一个「坐直」的时刻,赶过去就没有了。
重点二是最后那两行算术。很多人会把它当收尾一带而过,那是最亏的 ——  没有它,312 就只是一个背下来的数字;有了它,学员当场就能自己量任何一个算子。 「算力再翻十倍这行代码也不会快」和「边长 256 的矩阵乘是带宽受限」 这两句,一句都不要省。
重点三是最后那四十秒 —— 给「算子融合」这条线起名字。 它必须紧接在那两行算术后面说,不能挪到别处: 台下刚亲手算出「分母决定一切」,这时候说「所以办法就是把分母做小」, 是他们自己推出来的;换个位置说,就成了一句要背的口号
这一讲只有一张图(312 那张)—— 2026-09-03 删掉了原来那张 「三格落点图」,它三格里两格是复述、一格在说「这里没信息」。 时间省下来给最后那四十秒(给算子融合起名字)。 收口,指课件最后那条蓝线: 「这一节到此结束,你手上多了一把尺子 ——  它只回答一个问题:这个算子卡在带宽还是卡在算力。」
然后立刻抛钩子:「下一节把它用在一个真算子上, 那个算子会给出全课第一个大数 —— 融合前后,强度差一千倍。
02

⭐ 就是从这儿开始不同:同一次取数,一边路上 4 个决定,一边 0 个

⏱ 10 分钟
这一讲要留下什么 4 对 0。同一次「从 HBM 取一块数」,GPU 那条路上有 四个只有跑起来才知道答案的决策点,TPU 那条路上一个都没有
开场那句「只有一个成因」,在这一讲第一次变成看得见的东西

讲稿

滚到第 2 节开头,停在那张访存路径图(fig2-1)。 这张图是全课的中心,值得在屏幕上多停一会儿。

上一节的结论是两边胃口一样大。这一节说的是 ——  吃法完全不同。

第一个决策点要单独解释一句,别当术语滑过去。
「coalescing 就是合并访存:一条 load 发出去,warp 里那三十二个线程 各算出一个地址,硬件得把它们合并成尽可能少的内存事务。」
再补一句量级:合得好不好,差八倍带宽 —— 而且只有跑起来才知道, 因为编译器不知道你的地址是怎么算出来的。」
这一讲最容易讲砸的方式:讲成「TPU 没有 cache」。 那是把一整套机制缩成一个零件 ——  而且第一个决策点根本不是 cache,是 warp 那三十二个地址能不能合并。
要交的是「4 对 0」,不是「有没有 cache」。

我们不看任何参数,就跟着一次取数走一遍: 从 HBM 里拿一块数进片上。这一路要做几个决定,每个决定是谁做的、什么时候做的。

先只讲左半边(GPU)。一个决策点一个决策点地指, 每指一个说一句「这个答案,编译的时候不存在」。

左边 GPU。一个 warp 三十二个线程,各给出一个地址。 第一个决策点:这些地址能合并成几条 cache line?—— 跑起来才知道。

接着走到 L1,第二个决策点:命中还是没命中?再走到 L2, 第三个:命中还是没命中?没命中要换出一块,第四个:换谁出去?

四个。而且注意 —— 这四个我一个都没说「慢」。 红色的意思不是慢,是「跑起来才知道」

切到右半边(TPU)。这边节奏要快,因为它没有东西可数。

右边 TPU。编译期:XLA 决定每个数组切成 8 乘 128 的块。编译期:决定什么时候搬、 搬多大一块。编译期:算准 VMEM 装不装得下。然后运行时只剩两件事 ——  DMA 整块搬过去,向量单元直接取

路上零个运行时决策点。而且下面那一格是空的:没有 cache 这一层 —— 所以也就没有命中、未命中、替换这些概念。

这里停死,念图上那句红字:「四个,对零个。」 这是全课的中心,第 0 节那个论点在这一刻第一次落地。 念完不要解释,让它挂在屏幕上。

那绿色是什么意思?绿的也不是「快」,是「写代码的时候就已经定死了」。

所以两边的分工从这里就定了。GPU 那一侧,把这几个决定交给硬件在运行时处理 —— 也就是交给了,交给写 kernel 的那只手,你能做的是让硬件更容易猜对: 访问连续、避开 bank、手工 tiling。
TPU 那一侧,交给编译器在编译期算准 —— 也就是接受它的脾气

翻回第 0 节那个绿框「这门课不比谁强」,或者直接说这一句: 这不是谁更聪明 —— 是两条路线,各自靠一样东西。 一定要说,它挡掉后面所有「所以谁更好」的提问。

GPU 靠运行时适应 —— 形状对不对都能跑起来; 代价是命中率要跑起来才知道,不好的时候只能反复试
TPU 靠编译期算准 —— 形状提前定死,一个决策周期都不浪费; 代价是运行时没有补救手段,形状不对只能回去改代码

这里加一句,它是 §0 到 §2 那条线的收口「到这儿,『处处留一手』才第一次不是一个形容词 ——  它就是这四个决策点,每一个都是真实存在的电路, 这一次的地址合不合并、命不命中,它都在那儿等着被问。」 —— 开场那句抽象的话,在这一刻变成了硬件成本。
说到这儿就停,不要接「所以省下来的面积给了谁」 —— 那个方向本课撤回过,没有出处。

—— 后面第 3 节、第 5 节看到的每一样东西,都是这一句的后果。

lane / sublane 这一段不在这儿讲了 —— 它搬到了 3.3 开头(那儿才第一次真用上)。 这一讲到这里就收,直接滚到 2.3。 滚到 2.3,停在 Flash 那张对照图(fig2-3)。

判据有了,坐标有了。现在挑一个算子跑到底 —— FlashAttention。 理由很实际:它是今天每个大模型都要跑的那一步, 而且它正好卡在 312 的错误一侧,所以两边都必须动手。

先记一个数。一个头,128K 序列,bf16,那个注意力分数矩阵是 整整 32 GiB

给一个对照再停一下:一个 v7 device 一共只有 94.74 GiB 可分配 —— 装不下三个头。而模型有 128 个头。

朴素写法是怎么走的?算出分数矩阵 S,写回 HBM;读回来做 softmax, 再写回去;再读出来乘 V。一个头,来回搬了四趟 32 GiB —— 而真正的乘加,只有那两次矩阵乘。

Flash 怎么做的?把 Q、K、V 按行切成块,一次只取一块。 这一块的 S 只在片上出现,不落地;softmax 改成 online 的, 只维护两个标量 —— running max 和 running sum;累加进输出块, S 当场丢掉,换下一块。

结果是:那个 seq 乘 seq 的矩阵,一次都没在 HBM 里出现过。

指第 ③ 栏。这一栏是这一节和第 3 节之间的桥。

第 ③ 栏问的是「那一块该多大」。两边问的是同一个问题,答案交给不同的人。
GPU:kernel 作者自己定,预算是 228 KB 的 shared memory, 块大小是个可调超参 —— 调错了也能跑,只是慢。
TPU:编译器定,预算是 64 MiB 的 VMEM,但形状必须对齐 8 乘 128 —— 对不齐就补零,而补出来的零照样占 VMEM、照样过 MXU。

滚到最后那张 312 的轴(fig2-4)。

现在把这笔账放回上一节那条线上。

朴素:算术强度 64。它每搬 1 个字节只算了 64 次; 而搬这 1 个字节的工夫,硬件本来能算 312 次 ——  差了近五倍,算力有八成在空等,死死卡在带宽上。

Flash:65,536。同样的 FLOP,搬的字节少了一千零二十四倍, 直接跨到右边,卡在算力上 —— 而那才是该卡的地方。

⚠️ 这句要跟着说:65,536 是理想上界。真实的 Flash 会重复读 K 和 V, 到不了这个数。但结论不变 —— 它是从 312 的左边跨到了右边。

先别急着滚过去 —— 这张轴下面新加了一个绿框 「那它过了片上那道门槛吗」,两分钟,讲完再进橙框。

刚才说 Flash「跨到右边」,跨过的是 HBM 那条线 —— 312。
可片上还有一条:共享内存是 64。

⚠️ 这里必须主动点破,不要等人问: 屏幕上现在有两个 64 —— 轴上那个是朴素注意力的强度, 新框里这个是共享内存那一层的门槛一个是算子的,一个是硬件的,数字撞上纯属巧合。 不点破,台下一定当成同一个数。

而融合干的事,恰恰是把字节从 HBM 挪到片上—— 所以那道除法得拿新的分母,再做一遍。

指绿框里那五行推导,一行一行念,不要跳

设块的行数 Br、列数 Bc,head_dim 是 d。
每走一步,从共享内存读进 K 块和 V 块,各 Bc 乘 d。Q 那一块一直待在寄存器里不动。
算的是两次矩阵乘,4 乘 Br 乘 Bc 乘 d 次。搬的是 4 乘 Bc 乘 d 字节。

约一下这一步停一秒再说答案片上强度就等于 Br —— 块的行数。 让它自己浮出来,比直接报数有用得多。

Br 取 128,就是 128 对 64,过关
Br 取 32,就是 32 对 64 —— 在片上重新变成带宽受限

然后把锚点给他们:真实的 FlashAttention kernel, 块行数一律取 64 或者 128。不是巧合,是这道除法逼出来的。

⚠️ 限定要说:这笔账只数了共享内存那一层, softmax 的向量运算和寄存器那一层都没算。它给的是量级,不是精确值。

⚠️ TPU 那一侧的分子是 1,153.5,不是 2,307 —— VMEM 是每个 core 一块, 分母是 per core,分子就得跟着取 per core。这就是那四句问法的第三句, 说错一倍。

最后那行小字(TPU 侧)照念,一个字都不要加

TPU 那一侧算法完全一样,分子取 1,153.5(per core),分母换成 VMEM 带宽 ——  而这个数官方没有公开,我们不推测。
能说的只有方向:片上带宽比 HBM 高一个量级,门槛就低一个量级, 落到几十这一档,和 GPU 那个 64 同一个数量级。
—— 再加上 TPU 的工作台是 64 MiB,卡住它的不是这条线,是 3.4 那个刀宽。

滚到本节最后那个橙框「回头看一眼」。

好,回头看一眼刚才发生了什么。Flash 做的事, 就是把「算分数 → softmax → 再乘 V」这一串合成了一个 kernel —— 这就是上一节起的那个名字:算子融合。

这句停一下:它连一次乘加都没有省。 图上第 ①② 栏是一模一样的。省的全在分母。

所以到这儿,故事的前提齐了:判据 312朴素是 64所以必须改写

而真正的问题不是「要不要融合」 ——  是「这个融合谁来做」。Flash 这一次,是人写出来的, 一篇论文加一堆手写 kernel。第 5 节要问的就是:非得靠人吗。

可能被问到

GPU 也有 shared memory 啊,那不就是可编程的 scratchpad 吗?

是,而且这是这一节最该被问的问题 —— 先肯定他。 shared memory 确实就是软件管理的片上暂存,跟 VMEM 是同一类东西。

差别有两条,都得说:

TPU 只有这一类,GPU 是两类并存。图上那四个决策点全在 global load 这条路上 —— 不管你用不用 shared memory, 数据都得先从 HBM 经过那条路。你没法把它绕开。

谁来管它。shared memory 归kernel 作者管, VMEM 归编译器管。这正是第 ③ 栏那句「同一个问题,交给不同的人」, 也正是第 5 节的题目

⚠️ 别在这儿展开讲 shared memory 有多大、怎么分块 ——  第 3 节有专门的图。这里只说「绕不开」和「归谁管」两句。

bank conflict 为什么画成虚线的支路,不算进那四个?

因为 bank 是 shared memory 的分区机制,不在 global load 这条路上。 把它算进主路会让「4 对 0」这个对比失真 ——  而这个对比是整节课的中心,不能为了多一个数就模糊掉。

图上写了「走 shared 再加 1 个」,被问到就指那行小字, 说明我们是知道它、并且故意把它放在支路上的。

32 GiB 那个数怎么来的?

图上写了推导,指给他看,不要口算。 序列 128K 就是 2 的 17 次方,分数矩阵是 seq × seq, bf16 一个数 2 字节 —— (2¹⁷)² × 2 = 2³⁵ 字节 = 恰好 32 GiB。

「恰好」这个词可以强调一下:它不是估的,是正好撞在 2 的整数次幂上。

Flash 强度 65,536 是真的能达到吗?

达不到,而且这句必须我们自己先说,不能等人问。 真实的 Flash 分块之后会重复读 K 和 V,实际强度比上界低不少。

65,536 那个数的算法是「Q / K / V / O 各过一次」, 那是「一个字节都不多搬」的理想情况

但结论一点都不受影响:判据是 312,就算实际只有上界的百分之一 也还有 655,照样在右边。这一节要的是「跨过去了」,不是那个具体数。

⭐ 省下来的搬运,是不是拿多算的 FLOP 换的?那多出来的算「有用」吗

这是这一节第二好的问题,而且它是我们自己招来的 ——  橙框刚说完「一个 FLOP 都没省」,听着像白赚。先承认:通常是要付的。

付的形式有三种,性质完全不同,分开说:
重算 —— Flash 前向没有(图上 ①② 栏一样), 但反向有:中间矩阵没存,反向得重新算一次。
补零 —— 形状对不齐补出来的零,占空间、过 MXU、什么都不产出。 三种里最亏的,连换来了什么都说不上。
额外的向量活儿 —— online softmax 每块要重新缩放一次累加结果。 次数不多但落在向量单元上,数 FLOP 会低估它。

然后回答「算不算有用」:这个有正式口径,就叫 MFU 和 HFU ——  分子取模型数学上需要的叫 MFU,取硬件实际执行的叫 HFU, 重算和补零在 MFU 里一律不算有用
在这儿只说这一句,然后马上说「第 6 节我们会正经讲这两个词」。 —— 那一节讲的就是「两个数能不能比」,这两个口径是那节的正题。 在第 2 节展开,等于把第 6 节的内容提前讲了一半,两头都讲不透。

最后把它接回那条线,这才是这个问题的价值。但有一句必须先说准:
算术强度的分子是硬件实际执行的 FLOP ——  硬件不认识哪些是重算。所以判在 312 的哪一侧,用的是含重算的那个强度。

于是天花板有两块,谁低听谁的
有用吞吐 = min(峰值 × 有用比例,带宽 × 有用强度)
举个数:含重算的强度 600,一半是重算 ——  算力那块只剩 50% 峰值,带宽那块 300 ÷ 312 = 96%,取小的,只到屋顶线一半

⚠️ 这时候带宽也没用满(只有 312÷600 ≈ 52%)——  矩阵单元 100% 忙着,一半忙在垃圾上,带宽在旁边闲着。 这是「换过头了」的指纹。

所以规则:带宽受限区里多算的成本是, 一越过 312 就变成按比例扣产出 —— 这是一道悬崖,不是一条汇率曲线。 换到含重算的强度刚好顶到 312 就停。

别记成「强度不该超过 312 太多」 ——  有用的强度越高越好,没有上限(边长 4096 的矩阵乘强度 1,365、零浪费, 那是 100% 峰值)。该贴着 312 的只是你自己制造出来的那部分。

Flash 自己离那个坏情况很远:前向 2 次矩阵乘、反向 4 次, 它反向多重算 1 次 —— 7 比 6,有用比例 85.7%付 17% 的多算,换掉三个数量级的搬运。

课件里这一段是折叠的(橙框下面那条),不要主动展开 ——  被问到再翻。展开讲满是三分钟,而第 2 节一共只有七分钟。

那 TPU 上有没有 FlashAttention?还是说它不需要?

有,而且同样是必须的 —— 312 那条线两边一样, 朴素写法在 TPU 上一样卡在带宽上,那个 32 GiB 一样放不下。

差别在「那一块开多大」由谁决定:GPU 上是 kernel 作者调超参, TPU 上是编译器算,而且形状被 8 × 128 卡着。

这正是第 3.6 节整段要讲的事,所以这里只回答「有」,然后往前走。 —— ⛔ 不要在第 2 节讲 Pallas。

别讲什么

这一讲最容易跑偏的四个方向 ① 别把红色讲成「慢」。 这是这一节最要命的一次讲错 ——  一旦说成「GPU 这条路慢」,整节课就变成性能对比了。 红色的意思只有一个:跑起来才知道
② 别展开 cache 的工作原理。 组相联、替换策略、TLB ——  一个都不要碰。这一节只需要「有这么一层,它的答案在编译期不存在」。
③ 别在 lane / sublane 上超过一分钟。 它是词汇表, 是为了第 3 节能听懂而提前发的。在这儿讲透 8 × 128 的代价,第 3 节就没东西了。
④ 别讲 online softmax 的数学。 「只维护 running max 和 running sum」 这一句就够。推那个递推式要五分钟,而它跟硬件差别毫无关系。
节奏提示 七分钟,四张图。但时间不是平均分的 —— 大头在第一张。
fig2-1 给三分钟,其中「四个对零个」那个停顿要留够。 这是全课的中心,第 0 节那句论点在这一刻第一次有了证据。 后面第 3 节十四张图、第 5 节整节,都是在给这一张图做注脚。
fig2-2 一分钟,词汇表,讲完就走。
fig2-3 和 fig2-4 合起来三分钟:32 GiB 一个停顿, 64 对 65,536 一个停顿,「一个 FLOP 都没省」一个停顿
⚠️ 最容易超时的是 fig2-3 的第 ③ 栏 —— 那一栏信息很密, 很容易顺着讲进第 3 节的内容。它在这里的唯一任务是提出问题,不是回答。
⚠️ 第二容易超时的是最后那个折叠(融合要付什么代价 / MFU 对 HFU)。 它是折起来的,主线不讲 —— 但这个问题几乎一定会有人问, 而且问的往往是全场最懂的那个人。答法在「可能被问到」里写全了, 照着答,答完就走,别顺势开讲记账口径。
03上

拆开看:同一份复杂度,两边放在了不同的地方

⏱ 42 分钟
这一讲要留下什么 两件事,缺一不可。
第 2 节那个「4 对 0」不是一句口号 ——  从整颗芯片一路拆到一条指令,每一层都能指出它的物理来源。
每一层的差别都要能指回一个具体的部件或一条具体的指令 —— 说不出物理来源的差别,这一节不讲。
一句话记法 这半节只交零件 —— 装回去和收账在下半节。

讲稿 · 3.1 先看整颗

滚到第 3 节开头,停在 GPU 整颗那张(ms-g1)。 这一节十四张图,一开始就要立好节奏 —— 前面走快,钱花在 3.6。

前两节我们说了「两边胃口一样大」,也说了「吃法完全不同」。 这一节把两颗芯片真的拆开,看那个不同长在哪儿。

先看整颗。B200 一个封装里是两颗 die,中间用一条十 TB 每秒的桥连起来。 但你在 CUDA 里看到的是一个设备 —— 缝被藏起来了。

切到 TPU 那张(ms-t1)。两张图要连着指,它们是一对。

TPU v7 也是两颗 die。但它如实暴露成两个 device,两半各有自己的 九十六 G HBM,地址空间互不可见 —— 想用对面的,你得自己写一步。

停一拍:「缝在明处,还是缝在暗处。」 这就是第 2 节那个决定在封装这一层的样子。

藏起来的那一边,代价不会因为藏起来就消失 ——  你付了钱,只是不知道自己付了。跨 die 那一趟,带宽从二十一掉到十六点八, 数字在 3.2c 那张表里

⚠️ 图上那个 131,072 先别急着往峰值上乘。
它数的是格子 —— 两个 MXU 一共两乘二五六平方个。
每个格子一拍做两次乘加,这一步图上没有。

⚠️ 这句必须主动点破,别等人问: 「两次乘加」不是「一个乘加一个加、一共两次」。
一次乘加本身就是一乘一加 —— a 乘 b 再加 c,一个周期里一起做完,算两个 FLOP
所以一格一拍是四个 FLOP,不是两个。 这一步听岔了,后面整条链就差一倍。 在白板上写:1 次乘加 = 1 乘 + 1 加 = 2 FLOP;2 次乘加 = 4 FLOP。

把错算式念出来,别只说「会算错」: 131,072 乘 2 个 core 乘 2 FLOP 乘 2.2 GHz,得 1,153 ——  刚好是图头那个 2,307 的一半。少的就是「每格两次」那一步。

如果有人当场追问「那正确的链呢」, 3.4 现在补了一句桥,可以先给他一句预告: 「图上那个是格子数,3.4 那个 262,144 是每 MXU 每周期的 FLOP ——  一除就是每格 4 FLOP,也就是两次乘加。」 不要在 3.1 就展开算,节奏会塌。

讲稿 · 3.2 把一个核拆开

往下滚,SM 那张(ms-g2)。

再往里一层。先看 GPU 有什么,再看 TPU 没有什么 ——  顺序不能反,反了就会以为 TPU 是个简化版。

一个 SM 里:warp 调度器、二百五十六 KB 寄存器堆、 二百二十八 KB 的 L1 加共享内存、记分板。 这些部件加起来只干一件事 —— 在运行时动态地藏住延迟和分支。

切到 TPU 那张(ms-t2)。指右边那五个空格。

TPU 这边,这五样一样都没有

但重点不是「少了五样」,是这五样其实是一件事要不要在运行时换任务跑?答「要」,你就必须同时驻留几十份上下文、 必须有东西挑下一个、必须知道谁的数据到了 —— 五样缺一不可。 答「不要」,五样同时失去存在的理由

这句是这一小节的落点,值得停: 「有没有 cache」和「有没有 warp 调度器」不是两件事, 是同一个决定的两个面。

—— 而第 2 节主路那四个红框,就是答「要」之后的必然产物。

讲稿 · 3.2b 「GPU 片上明明更多啊」

滚到那张十行 SRAM 表。这一段没有图,全靠表。 语速可以快,但三个口径那个框要停。

这里必须正面接一个反问:「你说 TPU 把面积给了暂存, 可 GPU 片上 SRAM 明明更多啊。」

—— 他是对的。我们把两边的片上 SRAM 一格不漏地列出来: GPU 二百三十一 MiB,TPU 一百三十四 MiB,GPU 多百分之七十三。 这一条别嘴硬。

承认完,再翻面。这个顺序很重要 ——  先认下来,后面那两个口径才听得进去。

但同一张表可以读出三个方向不同的结论,看你在问什么。

第一个口径:一共多少 SRAM —— GPU 赢。
第二个:只数「软件当数据暂存用的那一行」——  TPU 一百三十对三十七,约三点五倍。
第三个,也是真正有用的那个一个计算单元能当成一整块连续工作台用的,有多大? —— TPU 六十四 MiB,GPU 二百二十七 KB,二百八十九倍。

指那句「GPU 那 37 MiB 不是一整块」。

为什么差这么多?因为 GPU 那三十七 MiB 不是一整块, 是一百四十八个互相看不见的小岛,每个二百五十六 KB, 而里面 L1 还占着一部分 —— 一个线程块能显式管到的最多二百二十七 KB。

这里停死。「二百八十九倍」这个数, 3.6 决定两边把块切多大的时候会直接用上。

还有一句必须说:第一个口径虽然对,却最容易把人带沟里。 GPU 领先的那九十七 MiB,几乎全来自 L2 那一行 —— 而 L2 是这张表里唯一一格你没法安排的空间。 你不能把一块权重「放进 L2 待着」,只能发访存指令,然后希望它还在

讲稿 · 3.2c 「那速度呢」

滚到带宽那张表。这一段只讲一个反直觉,两分钟。

上面回答的是「有多大、归谁管」,还差一半:跑多快。 补上之后会撞出一个几乎所有人都猜错的结果。

大多数人默认「cache 总该快一个数量级吧」。B200 的 L2 不是。 HBM 八 TB 每秒,L2 只有二十一,两点六倍。

停一下,让这个数落地。二点六倍。 这里马上要补一句,不然「只有二点六倍」会被听反。 台下很容易得出「片上存储没什么了不起」—— 完全相反。

二点六倍答的是一个很窄的问题这一个字节,命中 L2 比去 HBM 快多少。
换个问法就完全不同:每一字节容量能供多少带宽? —— L2 是用一千五百分之一的容量做到二点六倍带宽的, 两者一乘,它的带宽密度是 HBM 的四千倍。

⭐ 给一个能想象的说法,比倍数好记: 「把 HBM 整个读一遍要二十四毫秒;把 L2 整个读一遍,六微秒。」 ⭐⭐ 然后是这一段真正要留下的一句: 「这两个数的比值,恰好就是容量比 ——  也就是说,片上存储的本事全在『它小』这件事上。 它快不是因为它是 SRAM,是因为它小、离得近、线短。」
⛔ 说完立刻拉回来,别让它盖过主线: 「但优化的时候还是只能用二点六倍那个数 ——  因为你搬的是字节,不是容量。」

所以 L2 的价值不在「它本身快」,在「省掉一趟 HBM 往返」。 命中了,你拿到二点六倍;没命中,你什么都没省,还多绕了一圈。 —— 这就是为什么「让它落在 L2 里就好了」不是一个能依赖的策略。

为什么慢?因为 L2 是全片共享的,数据得穿过片上互连才到得了 SM。 而共享内存四点三倍、VMEM 高约一个数量级 ——  它们就贴在计算单元旁边。

这句是这两小节合起来的落点: 「真正快的那两块,恰好都是软件说了算的那一类。」

⚠️ TPU 那一行我们只给量级,不给数 ——  VMEM 带宽是未公开规格,官方只给容量。 能说的到此为止:它比 HBM 高约一个数量级。 别往这儿填一个精确数,也别说成某个整数倍 ——  HBM 带宽是公开的,写出倍数等于把那个未公开的数说出来了。

指最后那个 ⛔ 框「别把这两小节读成 TPU 赢了三局」。 这个框一定要念,它是整节课的护栏。

那三条领先都是真的,但每一条都有对价。
暂存又大又快,代价是没有兜底 ——  GPU 猜错只是慢一点,TPU 编译器切错了就是真的停在那儿等
GPU 那两个三十七 MiB 看着「没用在数据上」, 它买的是别的东西:形状不规则、长度会变、分支不确定的活,照样能跑得不太难看。

—— 所以真正的结论不是「谁的片上存储更好」,是这一句: 两边都在处理同一个东西 —— 不确定性,只是把它放在了不同的地方。 GPU 花硅去兜住它,TPU 把它消灭在编译期

讲稿 · 3.2d 「那等多久呢」

滚到延迟阶梯那张图。三到四分钟。这是上半节的落点,别赶。

容量讲完了,带宽也讲完了。还差第三个轴:等多久。 而这个轴,是三个里面唯一一个 GPU 自己也答不上来的。

先把问题原样抛给台下,别急着给数「一个 warp 要一块数据,L1 里没有,去 L2 查,L2 也没有,再去显存搬 —— 这一串下来要多久?」

看图。L1 命中,三四十拍。L2 命中,二百七十三拍。一路到显存,六百五十九拍。 —— 同一条 load 指令,在源码里长得一模一样。

停一下。三十拍到六百五十九拍,二十倍。

这里要拦一个几乎人人都会犯的算法「L1 查一次,加 L2 查一次,加 HBM 搬一次」—— 不能这么加。 六百五十九是一次全没命中的端到端耗时,前面那两次扑空已经含在里面了。

⭐ 然后是这一小节真正要问的那句: 「每种情况耗时都不一样,那这东西怎么提前规划?」
—— 答案很干脆:规划不了,所以 GPU 根本不规划。

它的三条对策,没有一条在试图算准。
第一,超量线程 —— 一个 SM 上驻着几十个 warp,这个卡住就切下一个。 延迟不是被消除的,是被别人的活盖住的
第二,记分板 —— 硬件在运行时盯着操作数到没到,到了才发。
第三,缓存本身就是一台猜测机器 —— 它赌你还会再用一次。

TPU 走的是反方向:把不确定性从源头删掉。 没有硬件缓存,就没有缓存缺失,也就没有可变延迟。 VMEM 里的数在那儿,不是因为运气好,是因为编译器自己发的那条 DMA 把它放在那儿的 —— 而且它也知道该在第几拍等。

⭐⭐ 这一句是上半节的落点,念慢一点: 「GPU 用『总有别的活可干』来盖住不确定的延迟; TPU 用『把不确定性删掉』来避免它。 一个是统计学的答案,一个是确定性的答案。」
—— ⭐ 紧接一句:「所以 TPU 没有 L2 不是缺功能。 缓存是一台猜测机器,而一台编译期就把每一拍排好的机器,不需要猜。」

顺手送台下一条实用的这也解释了为什么两边的「调优」没有一样东西是重合的。 GPU 上你调的是「怎么让别的活足够多」—— 占用率、warp 数、访存合并; TPU 上你调的是「怎么让编译器排得开」—— 对齐、分块、双缓冲。 同一个词叫调优,动的旋钮一样都不挨着。

⚠️ 最后别让它变成「两边越走越远」「现在追峰值的 CUDA kernel,异步拷贝、显式 barrier、自己管双缓冲 —— 那就是在 GPU 上手写一个静态调度。 一旦你真的要峰值,猜就不够用了。」
⭐ 这句给 3.5 那几个框埋了扣,讲到那儿会回收。

⚠️ 数的口径要交代一句:这组数是 H100 和消费级 Blackwell 上量的, B200 没有同类公开数据。量级和形状可以借,别当成 B200 的实测值。

讲稿 · 3.3 并行层级

先停在 3.3 开头那张寄存器形状图(fig2-2)。 一分钟讲完,它是词汇表不是内容 —— 但后面 3.3 和 3.4 全靠它说话。

先插一对词。下面要谈「一个线程其实是一条什么」,还有 3.4 那个「八乘一百二十八」, 都得先有 TPU 那边的坐标词,而这两个词的字面会骗人

两步就能把画面立起来。
第一步,先只看一条:一个 sublane 就是横着的一条, 128 格那么长,512 字节 —— 这是 VMEM 的架构字长,不是凑出来的数。
第二步,再叠 8 条:就是一个向量寄存器,8 × 128 = 1,024 格。

两个词到这儿各归各位:lane 量的是长度,第几格,0 到 127; sublane 量的是第几条,0 到 7。
—— 「对齐 8 乘 128」就是大白话:竖着凑够 8 条,横着凑够 128 格。

⚠️ 名字骗人的地方在这里:sublane 不是 lane 的一小段, 它是横跨全部 128 条 lane 的一整条。「sub」说的是第二维,不是更小。

这里可以轻松一点:「几乎所有人第一次都理解反, 包括我。」承认自己也搞反过,比强调「别搞反」有用。

记法只有一句:最内维是 lane,次内维是 sublane。

然后是这张图真正要留的东西:XLA 故意把内存布局做成跟寄存器一样的形状。 搬进来就能直接算,一次重排都不用。

这里有个说法我讲错过,别照老版本念「一行在内存里不再连续」—— 那不是代价,那正是目的。 一段一百二十八个元素正好铺满一百二十八条 lane,这是要的效果。」

真正的代价是另一样:这个形状被钉死了。 三笔,都能点名 —— 换最内维要真搬数据(分块绑在最后两维上, 转置和某些 reshape 不是改一下步长,是物理搬运);不整除就补零, 补出来的零占 VMEM、过 MXU、还算进 FLOP;跟外部交接要重排 这笔钱第 3 节要付。

顺手接一句对照,它把这一段接回主线「GPU 那边的合并访存也想要连续 —— 但那是软约束, 没对齐照样跑,只是慢。这边的分块是硬约束 ——  形状不对,要么补零,要么插一个重排。一边罚款,一边直接不让过。
这一节先不要滚图。头两分钟只说话, 推到「六层」再上第一张图

刚才少掉的是硅片上的部件。往上一层,编程模型里也会少掉东西同一个原因,第二次露面。

这一节我先归到一个问题上。

停一拍,这句要慢「你写 GPU 代码的时候, 不知道这堆活会落在哪个 SM 上,也不知道什么时候落 ——  是硬件在运行的时候才决定的。」 白板上就写四个字:「运行时才定」

既然不知道,你就没法点名写不出「让三号 SM 干这个」。

你能做的只剩一件事:描述关系哪些活必须待在一块儿,哪些落哪儿都行。

「必须待在一块儿」的那一批,就是一个 block。

为什么是「必须」?因为这批活之间要互相传中间结果。 而中间结果放在 SM 自己身上那块小内存里,不在外面。
所以一批活一旦落到某个 SM 上,就不许再搬走 —— 搬走了,中间结果就找不着了。

反过来,互相不用说话的活,落哪儿都行。全部加起来,叫 grid

这两层是你说了算的。 你写代码,其实就是在说两句话:这些必须在一起,总共这么多。

硬件那边还有它自己的分组,理由完全不一样,是为了省电路

取一条指令很贵。那就让三十二份数据共用同一条指令,一起算。 这三十二个绑成一捆,就是 warp这个数你改不了,硬件焊死的。

到这儿四层齐了:底下两层硬件定死,上面两层你定。

剩下两层是后来补的,各被一个很具体的麻烦逼出来
warp group —— 新一代的矩阵指令,要四捆一起发才凑得出形状。
cluster —— 相邻几个 SM 想直接够到对方那块小内存,不想绕远路。

这里补一刀,效果很好「而『绕远路』是绕哪儿?就是绕那个只比显存快 2.6 倍的 L2。」 —— 3.2c 那个反直觉的数,在这儿变成了一整层编程模型。 前面埋的数在后面变成结构,台下是听得出来的。 现在才滚到第一张图(每一层被什么逼出来)。 指「被什么逼出来」那一列,别念规模那一列。 这一节的落点,说完停两秒「这六层,全是从刚才那句『不知道』长出来的。」 ⛔ 图注最后一句已经是「=运行时适应的组织成本」, 别再念一遍,念了就是同一句说两次。 滚到 ms-t3。这一段指的是空格,不是格子里的字。

而 TPU 那边,编译的时候就已经把一切定死了不存在「不知道」。 所以这一整套语言,它一层都不需要。

⚠️ 顺手把一个陷阱按住两边的层根本不在回答同一个问题。 GPU 那些层是用来描述「谁必须在一起」的,TPU 压根不需要描述这件事。 所以这张按问题排,不按名词排。

空的第一格:没有「一组线程」。GPU 有 thread block;TPU 没有这个中间概念。
空的第二格:没有「换一个跑」。GPU 有四个 warp 调度器、六十四个 warp 槽; TPU 一个都没有。

—— 空的那两行,正好是 3.2 里少掉的那五样在编程模型上的投影。 硅片上少了部件,代码里就少了概念。

三处「不对齐」里,现场只讲第一条,另外两条留给提问: 「一个 SM 上同时住着好几个 block,它们挨着,但互相看不见对方那块小内存。 第 5 节讲的『占用率』,量的就是这个 SM 还能塞下几批。」 滚到「一个 SM 拆开」那张四格图。

再往里拆一层。「一个 SM」到这儿为止一直是个黑盒。 里面是四个格子,每个格子自己一套调度器和寄存器。 三十二是一个格子的宽度;一百二十八是四个格子各出一捆。

这里有个问题值得单独说:一个线程到底独占什么。

算的部件,它一样都不占。 CUDA core 也好、Tensor Core 也好,都不是分给谁的 ——  它们是流水线上的一个位置,哪一捆这一拍发了指令就用这一拍,下一拍换别人。

它真正独占的只有寄存器。一批活落到 SM 上就划走,跑完才还,中途从不搬动。

这一条同时解释了两件事,两句连着说「正因为寄存器不搬动,换一捆上来才是零成本的; 也正因为独占,先卡住你的一定是寄存器不够,不是算的部件不够。」

三十二位、四个字节、上限二百五十五、按八取整 ——  这些全在折叠里,现场一个都别念。

滚到最后一张(三个都叫「线程」)。

最后回到最底下那一格。一捆是三十二个线程, 每个线程在这一捆里有个编号,零到三十一那个位置,就是一条 lane。

所以 GPU 上也有 lane,硬件上就是一条 SIMD 通道 ——  跟 TPU 那边的 lane 是同一样东西。

这一节最后一句,说完就走「差别只在一件事 —— GPU 的 lane 有名字,TPU 的没有。 GPU 那条你能对它单独写 if;TPU 那条你写不出『第三十七号 lane 干点别的』。 所以 CUDA 说的线程,是一条有名字的 SIMD lane。」

讲稿 · 3.3a 那一条 lane 在一次真矩阵乘里干什么

还停在 3.3 那张「线程其实是一条 lane」的图上,别往下滚。 先主动打掉一个画面,台下十个有九个脑子里是它: 「一个线程负责输出矩阵里的一格。」
这个画面在没有 Tensor Core 的年代是对的,在今天是错的。

先讲对的那个年代。一个线程就是输出矩阵里一格的所有者 ——  它自己跑那个 K 循环,从共享内存读 A 的一段、B 的一段进自己的寄存器, 乘加,累加在自己手上,最后写回。搬运和计算,它都干。

然后把它推翻,这一句要慢:
「用上 Tensor Core 之后,矩阵乘指令是整个 warp 一起发的一条指令。 它的操作数散在三十二个线程的寄存器里,每人拿一小撮。 单个线程,自己算不出任何一个完整的输出元素。」
要具体就给一条:一条典型的指令里,每个线程交出 A 的八个数、 B 的四个数、累加器四个数。就这些。
⭐⭐ 这一段的落点: 「所以『一个线程负责哪一格』这个问题,到 Tensor Core 上就失去意义了。 线程退化成两样东西 —— 一份寄存器的持有者, 和发那条指令的三十二分之一。」
—— 这才是「一个线程其实是一条 lane」的完整含义。
指下面那个「到底是不是 SIMD」的框。

这一问答案是分层的,两个层面结论相反。 硬件上是 SIMD —— 一个 warp 的三十二条 lane 共享取指、译码、发射, 同一拍执行同一条指令。编程模型上不是 —— 你写标量代码, 而且 Volta 起每个线程真有自己的程序计数器,可以各走各的分支。

⭐ 一句话收: 「SIMT 就是:用标量的写法,跑在 SIMD 的硬件上。 代价写在分支上 —— 真发散了,各条分支只能轮流走。」
然后接回主线:「而这一整套,就是『靠运行时适应』要付的钱。 TPU 不做这个决定,所以它那边连『线程』这个词都没有。」
⚠️ 被问到搬运再展开,别主动讲(会吃掉两分钟): 线程管不管搬运?管,但这一半正在退场 ——  Ampere 之前线程自己搬、搬的时候被占住;Ampere 起 cp.async 绕过寄存器; Hopper 起 TMA 一个线程发一条描述符就搬一整块,另外三十一个不参与。

讲稿 · 3.3b 那套东西具体是什么(⚠️ 可整段跳)

滚到 3.3b,先指主线上那条蓝线的一句话定义 (「记分板就是一张表……」)。这一句必须念,十五秒。
底下那个折叠是机制(四分钟,全讲最容易超时的一段)——  台下没人追问就别展开,直接跳到 3.4。
这一句念出口就行,别加料: 「记分板就是一张表,记着每条指令要的操作数到齐了没有; 调度器每一拍扫一遍,谁齐了让谁发射。」
—— 然后立刻接回主线:「TPU 那边这张表整个不存在。」

刚才说了两遍「GPU 把 lane 暴露给你,代价是一整套东西」这一小节就回答一句话:那一整套,具体是哪几样。 三样,各管一个很窄的问题。

三个问题先一口气报出来,报完再逐个展开 ——  学员知道一共几件、现在在第几件,才不会中途掉线:
「一,谁现在能发指令;二,谁跟谁怎么对齐; 三,数据怎么在不占用线程的前提下就位。」
最后一件事,指着 3.3b 那张三代对照图。这是这一小节的钩子,别省。

线程管搬运的那一半,正在退场 —— 三代,一代剥一层。

Ampere 之前,线程自己搬:从全局到寄存器,再到共享内存。 搬的时候它就被占住了,什么也干不了。
Ampere 起,多了一条指令,让数据绕过寄存器直通共享内存 —— 线程不用当二传手了。
Hopper 起更狠:一个线程发一条描述符,就能搬一整块另外三十一个完全不参与。

这里要把三代收成一句,慢点说「三代下来,线程从『搬运工加计算工』,被剥成了一个寄存器的容器。」 —— 它跟前面那句「线程算不出任何一个完整的输出元素」是同一件事的两半: 一半剥走了计算,一半剥走了搬运。 不要在这儿接「所以 GPU 在往 TPU 靠」。 这条趋势线是 §5.4b 的,那里有完整的证据和一条重要的限定 (挪的是「怎么搬」,不是「谁决定」)。这儿只留钩子。

—— 而线程被剥空之后,真正决定「一次能吃多大一口」的就不再是线程了, 是指令那一维两边差 16 倍,3.4 会当场量给你看。

但先别跳过去。下一小节 3.3b 要先把账单打开。

上面这一整套 —— 让你写标量、硬件替你凑成 SIMD、发散了替你排队、 再替你在几十批活之间来回换 —— 「靠运行时适应」这五个字,到底要花多少钱? 下一小节把它拆成三套具体的电路,一样一样看。

第一样,记分板。先送一个记得住的钩子: 这个名字是从一九六四年那台 CDC 6600 借来的 ——  那是第一台指令能不按顺序完成的机器,它里面那个部件的正式名字就叫 Scoreboard。 (为什么挑这个字、以及它和现在这块已经不是一回事, 折在课件里,别现场展开。)

回到 GPU。调度器每一拍要挑一个「就绪」的 warp 发指令 ——  「就绪」是谁判的?NVIDIA 把指令按一条线劈成两半: 编译器知不知道它要跑多久。
知道的那一半根本不用硬件管,编译器在指令里直接写「停几拍」。
不知道的那一半 —— 访存、特殊函数、矩阵指令 —— 才是记分板: 每个 warp 六个计数器,发出去加一、写回来减一; 等它的那条指令带个掩码,写明要等哪几号归零。

停:「所以它不是一张大表,是六个很小的计数器。 它只回答一个问题 —— 这个 warp 现在能不能发指令。」

第二样一句话带过:SM 里一小组硬件 barrier,warp 陆续到达,数满全放行。 机制折在下面,回查用。

报了「三样」就三样都要有下落 ——  说「第二样跳过」等于当众漏掉一件。只念这一条就够「在 barrier 上等着的 warp 不腾位子 ——  所以同步密集的 kernel,占用率不会因为『大家都在等』而变好看。」

第三样值一张图。

滚到那张四行的数据通路图。只指中转那一列,别逐行念。

四行,两头一模一样 —— 左边都是全局内存,右边都是共享内存加计算单元。 在变的只有中间那一列。
最早,数据得在寄存器里过一道,地址和循环全靠线程自己算;
Ampere 起,cp.async 把寄存器那一道砍掉了,地址还是自己算;
Hopper 的 TMA,一个线程填一张描述符发出去就走 ——  地址生成整个交给硬件

停:「走到这一步,它和 TPU 那条编译器发的 DMA 已经是同一类东西了。剩下的分歧只有一个 ——  这条 DMA 是运行时某个线程发的,还是编译期就排好的一步。」

—— 落点:记分板管「谁能发指令」,barrier 管「谁跟谁对齐」, TMA 管「数据怎么不占用线程就位」。三套加起来,就是「跑起来才决定」那笔账的明细。 TPU 一样都不需要 —— 不是它简陋,是这三个问题它在编译期就全答完了。

讲稿 · 3.3c ⭐ 矩阵乘到底按什么顺序跑

滚到那张「三种铺法」的大图,先只停在最上面那一栏(朴素)五到六分钟。这是 §3 下半段的地基,千万别赶。

两边的部件到这儿都拆完了。但有一件更基本的事,我一直没说: 一次矩阵乘,在硬件上究竟是按什么顺序算的。

先别看图,先让台下自己说一遍。 问:「矩阵乘怎么算?」—— 十个人有十个会说: 拿左边第一行、右边第一列,对应相乘加起来,得到左上角那个数。」
让他们说出口,这一步很重要。

对。数学上完全正确。 —— 而真实硬件,无论 GPU 还是 TPU,一个都不这么算。

停。让这句话空一拍。

先数一下朴素算法这笔账。看图上第一栏。
第一步:A 的整个第 0 行乘 B 的整个第 0 列,得到 c₀₀,写出去。
第二步:还是 A 第 0 行,换 B 第 1 列 ——  注意,A 第 0 行那三个数,你又读了一遍。
四步走完:A 的六个数各读两遍,B 的六个数各读两遍,总共读 24 次, 只换来 12 次乘加。

⭐ 这里给一句好记的:「读两次,才干一次活。」 —— 而且注意:这个算法确实只要一个累加器, 台下如果有人问「那不是挺省的吗」,答案是:省错了地方。 滚到第二栏(GPU)。

硬件干的是另一件事:把 k 挪到最外层。
第一步,k 等于 0:只读两样 —— A 的第 0 列(两个数)、 B 的第 0 行(两个数),一共四个数。
然后这四个数两两配对,四次乘加, 分别加进四个格子里 —— 四个格子同时都动了。

第二步、第三步同理。三步走完,四个格子同时算完,一起倒出去。 —— 总共读 12 次,做 12 次乘加。整整好一倍。

⭐⭐ 这是整段最该被记住的一句,念慢: 「不是一个格子被反复用,是所有格子被同时慢慢填。」
顺带把那个具体问题答掉:每个格子恰好被更新 K 次, 左上和右下一模一样,没有谁多谁少。

那为什么不能只留一行累加器?看那条算式。
累加器是 M×N 的一块,K 每走一步,读进来 M+N 个数,能做 M×N 次乘加。 所以复用率就是 M×N 除以 M+N

这条算式最狠的读法:它被短边卡死。 「你留一整行,短边就是 1 —— N 开到一百万,复用率还是 1。 长的那一边一点忙都帮不上。」
128 乘 256 的块是多少?八十五。

所以这一句是这一节真正的结论: 累加器阵列不是用来装结果的,它是用来买复用的。 你多开一个累加器,就等于让搬进来的那批操作数多配对一次。 —— 这正是第 1 节那条算术强度的主线,第一次长在了硅上。

滚到第三栏(TPU)。

TPU 换了个方向。B 先装进阵列,装完就不动。 A 一行一行从左边流进去,部分和一路往下累加 ——  从底下出来的时候,已经是算完的一行 C 了。

⭐ 这里的关键一句: 「K 方向那几项求和,是在空间上完成的 ——  三个 PE 各做一次乘加,把部分和接力往下传。这几项根本不经过外部累加器。」 滚到最下面那张对照表。这是整节的落点。

把三栏收成一句:同一个数学,区别只在「哪个维度摊在空间上,哪个走时间」。
GPU 把 M 和 N 摊在空间上、让 K 走时间; TPU 把 K 和 N 摊在空间上、让 M 走时间。

⭐⭐ 最后这一句一定要说,它是往下三节的钥匙: 「这一个选择,决定了后面所有事 —— 累加器要多大、 tile 为什么是最核心的那个旋钮、以及 TPU 为什么非得让权重坐着不动。」

⚠️ 两条边界,讲的时候顺口带一下就行,别展开① 图上 GPU 那栏画的是算法级;指令级上一条 MMA 已经在内部把 K 的一小段 规约掉了 —— 形状一样,只是成批做。 ② TPU 那边外部累加器只在跨 K 分块时才用到,真机 K 常常几千, 阵列一次只吃得下 256。

讲稿 · 3.4 一条指令吃多大一块

这一节先别滚图。先把问题问出来,再滚 ms-g4。

上一节讲的是谁来发这条指令。这一节换一个问题 —— 一条指令发出去,一次能吃下多大一块?

听起来很内部,但它决定一件很外部的事 ——  你的 head_dim 选多少不吃亏。

现在滚 ms-g4,再切 ms-t4。两张图只看同一个位置: 求和的那一维有多深。

GPU 这边,一路五代指令,动员的线程越来越多、一次吃的块越来越大, 但求和那一维五代都没变,一直是十六

TPU 这边,MXU 是二百五十六乘二百五十六的脉动阵列,那一维就是二百五十六

停死,念这个数:「十六,对二百五十六。差十六倍。」 这是这一小节唯一要记住的数。

这一维有个名字,叫收缩维 ——  就是矩阵乘里被加掉的那一维,一条指令一次要沿着它累加多少项。 名字不重要,重要的是刚才那两个数。

差十六倍意味着什么?拿一个真形状套进去:注意力的 head_dim 常取 128。

GPU:一百二十八除以十六等于八,切成八条指令,一点不浪费
TPU:一百二十八对二百五十六,只喂满一半 —— 另一半空着。

那空着的那一半,找点别的活填进去不行吗?

不行。收缩维是一条累加链 ——  塞进去的任何别的数据都会被加进同一个部分和,谁也顶不上来

反过来问:那 GPU 为什么不吃这个亏?

不是因为它有调度器。调度器换的是 warp,填的是时间上的气泡 —— 它填不了收缩维里空着的那一半。 是因为它的 K 只有十六,几乎什么形状都是十六的整数倍。

⛔ 上面这句照着念。 早先的版本说「GPU 有调度器所以对形状不敏感」,那是错的 ——  台下懂 CUDA 的人一句话就能顶回来。

⚠️ 这不是哪边设计得差。GPU 敢把 K 做这么窄、把对齐这件事留到运行时收拾, 代价就是 3.2 里 TPU 少掉的那五样。 —— 「按谁的粒度对齐」这个问题,本来就有两个答案。

滚到 3.4 最后那张四条带的大图(图上标题是 「四条带,从『谁不动』一路走到『要重搬几次』」) (四条带:三种驻留 / 切块 / 乒乓 / 重搬账)。
⛔ 别四条带平均用力,按这个优先级
① 先讲第三条带(乒乓)—— 这是别人没讲明白的那一段。
「装一块权重要 256 个周期。但每个格子有两套权重寄存器 ——  一套在算,一套在装。所以那 256 藏在算的那 2048 里面, 只有第一块要干等。」
⭐ 然后立刻给判据:「藏得住的条件就一条:一段的行数要大于 256。」

② 再回到第一条带(三种驻留),一句话带过: 左驻、右驻、中间累加驻,区别只有一个 —— 谁不动。 TPU 走的是右驻。 而「谁不动」决定了哪两维是空间、哪一维是时间。

③ 第二、四条带留给追问168 块怎么来的、为什么 M 不能一口气流完(累加器要 128 MiB,比 VMEM 还大)、 段切得越碎右矩阵重搬越多次。 台下不问就不展开。

这张图的收口只有一句,一定要说「段开长,右矩阵少搬;段开长,累加器变大。所以这个旋钮拧到底, 还是被片上那口灶台顶住的 —— 跟 3.6 里 GPU 那堵墙是同一堵。」 ⚠️ 这里几乎一定有人举手算账: 「一条指令才十六深,怎么也凑不出两千二百五十 TFLOPS。」
提前拦一句:指令形状是「一次交代多少活」,每周期做多少乘加是另一回事。 要对账就报课件上那条:4,096 × 2 × 148 SM × 1.83 GHz = 2,218,官方 2,250,差 1.4%。 ⛔ 别顺着他那条错推法往下推。
指本小节末尾那条峰值链。这一段是给会算账的人的, 语速可以快,但结论要清楚。

顺手把 TPU 的峰值从头乘一遍,验证我们没在瞎说: 一个 MXU 一拍 262,144 次浮点运算,乘二点二 GHz,乘四个 MXU —— 等于 2,307 TFLOP/s。官方公布 2,307,一分不差。

⚠️ 这里有一条必须带走的跨代警告TPU 那个数里有一半来自「每个 cell 每周期做两次乘加」 ——  这是二百五十六乘二百五十六这几代才有的。 一百二十八乘一百二十八的老几代,每 cell 只做一次。 跨代沿用这个常数,会直接错一倍。

这一小节的收口,一定要说: 「能融多大,由两个数一起卡死 —— 一个是工作台多大(3.2b 那个 289 倍), 另一个就是这里的粒度多粗。3.6 切块的时候,这两个数会同时出现。」

讲稿 · 3.5 各自多出来的那一块

先别滚图。这一节的问题一句话就能问完, 问完再滚 ms-p37。整节一分半,不要展开。

上半节到这儿为止,数的都是「一边有、另一边没有」。 还有两块,是各自额外长出来的。

但这一节要问的不是「它们是什么」。慢一点,问这个: 「宣传页上写着支持的东西,你到底用不用得上?」
白板上就写四个字:「用不用得上」

这一节就两个例子,两边各一个。

现在滚 ms-p37。左边一个例子,右边一个例子, 下半张是两道门槛。

第一个例子在 GPU 这边,叫块量化。宣传口径是「B200 支持 FP4」。

这句话是真的。门槛在于 —— 带块量化的指令有两条通路, 而它们不在同一颗 die 上,B200 只有其中一条。 所以听的人默认「两条都有」,那就错了。

第二个例子在 TPU 这边,叫 SparseCore —— 一颗专门查嵌入表的核。 宣传口径是「TPU 有个专门查 embedding 表的核」。

这句话也是真的。门槛在于 —— 它只对「同一批行被反复查」的负载划算。 图上那个 duplication factor 量的就是这件事。

这里停一下,这是给这屋里的人的那一句「语言模型的重复度大约是一点零一 —— 一行几乎只查一次。 编译器根本不往那儿派,生产任务里它一次表都没查过。」 紧接着必须补一句,不能停在上面那里「但『用不上』不等于『没在用』—— 这颗核你天天在用。」

它在大模型训练里真正干的活是卸载集合通信 ——  把 All-Gather 和 Reduce-Scatter 从 TensorCore 手上接过去让 TensorCore 专心算,通信在旁边并行跑完。 这就是「算和通信重叠」在 TPU 上的落地方式之一。

如果有人追问「不规则访存到底怎么处理的」 ——  课件里有一张 SparseCore 拆开图台上别滚过去(会吃掉三分钟)。
口头给一句就够:「TensorCore 一次搬 (8,128) 一整块, SparseCore 一次只动 8 个数,而且地址可以是刚算出来的 ——  它分成十六个小单元各追各的地址,靠同时欠着很多次取数来扛延迟。」
要细节让他课后看那张图。
被追问粒度时,这两句不要说错
「(8,128) 是向量寄存器的形状,不是 DMA 的最小粒度。」
「SparseCore 在 v7 上的 DMA 粒度是 64 字节 ——  这个数是跑 get_tpu_info 直接报出来的,不是文档里的形容词。」
还有一个陷阱问题:「它操作 VMEM 还是 HBM」——  答「两块都碰,但它碰的那块 VMEM 是它自己的,不是 TensorCore 那块」。
有人要出处就报这两个,都是公开的:Google Cloud 那篇 《Training large models on Ironwood TPUs》把它单列成一条调优手段; MaxText 公开仓库的 XLA flag 库里就有 SparseCore All Gather。 ⛔ 不要报任何内部实测数字。 ⭐ 这一节的教训到这里才完整,慢慢说: 「宣传口径不只会让你用不上它说的那个能力 ——  还会让你看不见你已经在用的那部分。你拿『查表的核』这个名字去 profile 里 找查表,找不到,就以为这颗核在闲着。」

两个例子摆完,顺带能看出一件事:这两块东西连层级都不一样 —— 块量化是一条指令的变体,SparseCore 是物理上独立的一颗核。 「两边各多出一块」这个说法本身,也是宣传口径的产物。

两句宣传语都没说错 —— 错的是把「支持」读成「我的负载用得上」。

收口给一个动作,不要只给感慨: 「看到一个『支持 X』,先去找那道门槛写在哪一行。找不到, 就当它不适用于你。」
滚到上半节最后那个绿框「上半节的账」。 这一句是上半节全部的收获,慢慢说:
「五层拆下来,TPU 那边每一层都少了点东西 ——  但少掉的从来不是算的部件。少掉的是 cache、是记分板、 是那几层用来『等到跑起来再决定』的机构。 每一样都是为『不知道你要跑什么』准备的。」
—— 然后点破:「第 0 节那句话,到这里已经被验了五遍。」
⚠️ 这句必须说,它是全课中段唯一一次回到开场那句论点。 不说的话,台下走过整个 §3 都不会意识到自己一路在看同一件事的五个证据。
03下

⭐ 装回去:同一个 FlashAttention,两边并排走完一遍

⏱ 10 分钟
这一讲要留下什么 两次回收,一次比一次高。
3.6 末尾:六站里四站两边是一样的 ——  第 0 节那句「处处都不同,但只有一个成因」在这里从论点变成实证。
3.7:份数多 148 倍 × 每份小 128 倍,几乎正好抵消 ——  第 1 节开场那两个「同一量级」的峰值,到这里才算解释清楚。
一句话记法 上半节拆零件,这半节装回去,装完顺手把前面欠的两笔账还了。

讲稿 · 3.6 ⭐ 高潮:并排走完一遍

停在 3.6 标题那个 🍳 框上,先别滚到图。 这个比方要在所有数字之前建立画面 —— 顺序错了,后面全白讲。 开口先把这一节要答的两句话摆出来,就两句「这一锅能做多大?两边分别是被什么卡住的?」
白板上写四个字:「多大 · 撞哪」

上半节拆出来的全是零件。这一讲把零件装回去 ——  第 2 节挑的那个算子,现在真的在两块硬件上各跑一遍。

这里换一种讲法。先不给数字,给一个厨房。 (这个比方是讲砸过一次之后补的 —— 上一版全程只有术语, 学员脑子里立不起画面。照着念,别改。

把这道算子想成一道要三个步骤的菜:切、炒、装盘。

笨办法是这样:切完了,把半成品送回冷库;要炒了,再从冷库取出来; 炒完再送回去;要装盘了,又取一次。三个步骤,冷库来回跑四趟。

—— 而这道菜的麻烦在于:那个半成品特别大。 原料一共才一百多兆,半成品三十多个 G —— 比原料大二百五十六倍。 来回搬四趟就是一百多个 G 全花在路上,而这一路上一刀都没切。

停。「FlashAttention 干的事就一句话: 三个步骤在灶台上一口气做完,半成品根本不下灶台。」

这个比方只有三个本体,记住它们,后面就不用再翻译了:
冷库等于 HBM,大但远。
灶台等于片上暂存 —— GPU 的共享内存,TPU 的 VMEM,小但近。
刀有多宽,等于一条指令一次吃多大一块 —— 就是 3.4 那个十六对二百五十六。

回到开头那两句话,现在它们有画面了一锅能做多大,就是灶台有多大;被什么卡住,就是撞的是灶台还是刀。

滚到 ms-p38。比方铺完了,现在换真数字。

先回答一个还没正面回答的问题:为什么非分块不可? 第 2 节说「需求比供给多五倍」,那是个比值。这里给绝对量。

朴素写法:写 S、读 S、写 P、读 P ——  四趟,每趟三十二 GiB,合计一百二十八 GiB 的 HBM 往返。 而这一大笔里,一次乘法都没有。 对照一下:Q、K、V、O 加起来只有一百二十八 MiB

这里念图上那三笔账,一笔一笔念,别概括「计算量,完全不变。HBM 读写,省掉一整项。显存占用,从平方降到线性。」

你刚看完的这张图,讲的就是第 1 节和第 2 节那个词 —— 算子融合。 这次是全程摊开的样子。

🍳 回到比方:融合就是「三步在灶台上一口气做完,半成品不下灶台」。 左半边红的那一列,就是「每做一步送一趟冷库」;右半边绿的,就是灶台上做完。

这一句要慢:「所以那三笔账的形状, 是所有融合共有的,不是 FlashAttention 独有的 ——  FLOPs 一分不省,省的全在『中间产物不落地』这一行。」

⚠️ 也正因为它不改数学,它的天花板不由算法定,由硬件定。 能融多大,取决于片上放不放得下。 —— 接下来两条路要走的,说到底就是同一个融合在两块硬件上各能融到多大。

滚到 ms-g6,再到 ms-t6。这两张各一分钟, 不要逐站细讲 —— 它们的任务是让第三张图有依据。

先各走各的,走完再并排。这个顺序有意为之 ——  直接看对照表会以为两边处处不同,各走一遍才看得出绝大多数站是一样的。

GPU 侧五站,中间两站是缓存 —— 命中不命中,跑起来才知道。 这就是 2.1 主路那四个红框的物理来源。

TPU 侧这条路,中间那站不是缓存,是暂存。每一站只问一个问题: 这一步是谁决定的。—— 从头到尾没有一站的答案是「硬件自己看着办」。

滚到 ms-p39。⭐ 这是本节主图,也是全课主图之一。 给它三分钟,横着读。

现在把两条路并起来。一行是同一站,左边是什么,右边是什么。

逐行问「这一步是谁决定的」,会得到一个很干净的结果: 六行里,只有两行是结构性的。

停死,报这两行: 「第 ② 行 —— GPU 有一整层硬件缓存,TPU 没有。 第 ⑥ 行 —— 那个『来回』是谁安排的。」

第 ② 行在上半节 3.2 到 3.2c 已经讲透了。真正没展开过的是第 ⑥ 行。

这道菜要在两种炉子之间来回跳 —— 矩阵那个炉子和向量那个炉子GPU 是干活的时候临场挑,谁空了上谁;TPU 是提前排好班,几点几分谁上,一分不差。 —— 这一条就是第 5 节的全部主题,这里先埋个头。

⭐ 拿第 1 节那条判据在这儿数一遍六站里只有这两站是 「一边有、另一边整个没有」其余四站两边做的是同一件事别处不是没差,是那些差都只有百分之几,下一代就变了。

指图中间那块绿色面积。

回到 3.4 结尾那句:能融多大,是被两个数一起卡死的。

第一个数刚在图上画成了面积 —— 工作台多大。 GPU 一个线程块最多二百二十七 KB:装三块一百二十八见方的 bf16 tile 就是九十六 KB, 再留出双缓冲,基本就到顶了
TPU 那边六十四 MiB,是它的二百八十九倍 ——  容量在这一侧根本不是那个卡住人的东西。

第二个数是 3.4 那个粒度。GPU 收缩维十六,TPU 二百五十六。 同一个 head_dim 等于 128,在 GPU 上怎么切都齐,在 TPU 上只填满一半。

🍳 回到比方,这两堵墙就是一句话,这句要念完整「GPU 是灶台太小,只能一小块一小块地做,同一批料要反复往上端 ——  K 和 V 就是这样被重读的。 TPU 灶台大得根本用不完,它的问题换成了刀太宽 ——  一刀二百五十六宽,你的料只有一百二十八宽,砍下去半条刀刃是空的。」

—— 同一件事,两边撞的是不同那堵墙。 这也是为什么 3.4 说这两个数要一起看,单看哪个都会得出错的结论。

滚到 ms-p40,本节最后一张。一分半。

最后一张收账。不看站点,只看「你要写多少东西」 —— 这是这趟旅程留给写代码的人的那一面。

数学一样、FLOPs 一样、结果一样。差的只有一行 —— 谁来安排搬运。
GPU 侧六条要 kernel 作者亲手写:块多大、发异步拷贝、双缓冲、warp 分工、对齐、要不要占 L1。
TPU 侧只有两条声明

停:「右栏比左栏短,这件事本身就是结论。」

⭐ 走完一遍之后,第 0 节那句话兑现了六行里四行两边是一样的 ——  不是我们挑了个有利的例子,是把全程摊开数出来的。

而且那两行本来就是同一处 —— 有没有自动缓存、谁安排换手, 问的都是「这个决定要不要留到运行时做」。

⚠️ 这一句必须说,它是本节最容易被听反的地方: 「千万别把『右栏短』读成『TPU 更省事』。 那两条声明写错一个字,编译器不会替你兜底 ——  左栏是『你要做很多决定』,右栏是『你只有一次机会做对』。」

讲稿 · 3.7 把账合起来

滚到最后那张 ms-g8。两分钟,只讲右边那张账。

🍳 先给一句画面版:两颗芯片的刀刃总量几乎一样,切法完全不同 —— 一边切成很多把小刀,一边切成几把大刀。

⚠️ 看图的时候盯住右边那张账,同一把刀有两个尺寸,别混成一个
刀有多宽(一次吃多深)就是 3.4 那个收缩维,十六对二百五十六
一下能切下多少(每拍多少次乘加)是 1,024 对 131,072,差 128 倍

⭐ 这张图最值得记的是右边那个 148 ÷ 128这句要慢慢说「份数多 148 倍,每份小 128 倍 ——  两个数几乎抵消掉了。这才是两边峰值差不多的真正来源。」

它不是约等于,是精确等于:148 除以 128 等于 1.156, 而 606,208 除以 524,288也是 1.156

剩下那 1.16 倍的差,TPU 用时钟补回来 ——  二点二 GHz 对一点八三,高两成,2,307 反超 2,250

⚠️ 但这条链算的是 HGX 板上的 B200。 第 1 节开场那对是 2,307 对 2,500 —— 那是 NVL72 里的 GB200, 时钟约二点零六 GHz,只高不到一成,压不过那 1.16 倍于是变回第 1 节那个「GPU 高百分之八」。

这一节的总落点,念完就走: 「所以整节课那些倍数,说的全是『份的大小』,不是『算力大小』。 两边的硅在同一个量级上,只是一个切成 592 小份,一个切成 4 大份。」

可能被问到

⭐ 你说 GPU 片上 SRAM 多百分之七十三,又说 TPU 多三点五倍,到底哪个对?

两个都对,而且这个问题被问出来是好事 —— 它说明他在认真读表。 标准答法是先反问一句:「你要拿这个数干什么?」

问「这颗芯片一共有多少 SRAM」 —— 用总量,GPU 二百三十一对一百三十四。
问「我能把中间结果放在哪儿不落 HBM」 ——  只有第三个口径算数:一个计算单元能连续用的工作台, 六十四 MiB 对二百二十七 KB,二百八十九倍。

⚠️ 还有一个陷阱要自己先说破。那个「三点五倍」是只数 「软件显式管的暂存」那一行算出来的。 如果按表里「谁说了算」那一整列读,TMEM 和累加器也算软件管的, 全算进来是 134 对 74,只有 1.8 倍。
—— 所以报这类比值必须先说清数了哪几行。 这句话本身就是这一节的教学价值之一。

L2 一百二十六 MB 那么大,为什么不算「片上工作台」?

因为工作台的定义是「我能安排的空间」,而 L2 是这张表里唯一一格你安排不了的。

你不能写一条指令说「把这块权重放进 L2 待着」。 你只能发访存指令,然后希望它还在。 —— 而第 2 节那四个运行时决策点里,有两个(L2 命中、被挤出去)说的就是它。

⭐ 可以加一句把它接回主线:「把 L2 算进『我有多少片上空间』, 正是这门课从第 2 节起就在防的那个错。」

TPU 的 VMEM 到底多快?你为什么不给数?

因为它是未公开规格,官方规格页只给容量、不给带宽。 照实说,不要编。

能说的是:它比 HBM 高约一个数量级,而 GPU 的共享内存是四点三倍。 所以能确定的结论只有一句:各跟各的 HBM 比,TPU 这一层甩开得更多。 这句不依赖具体数值。

不要在现场估一个数,也不要说成某个整数倍。 HBM 带宽是公开的 —— 说出倍数,就等于把那个未公开的数说出来了。

收缩维只有十六,那 GPU 岂不是很吃亏?每次都要循环好多轮。

方向反了 —— 窄恰恰是它占便宜的地方。

K 等于十六意味着几乎什么形状都是它的整数倍喂满很容易,几乎不浪费。TPU 那边 K 等于二百五十六, head_dim 128 直接打对折。

循环轮数多不等于吃亏 —— 每一轮都是满的。 真正的代价在别处:要让一个通用调度器管住成百上千个细粒度的块, 就得养 warp 调度器、寄存器堆、记分板 ——  那就是 3.2 里 TPU 少掉的那五样。

⭐ 落点一句:「细粒度买来的是『什么都能跑』, 粗粒度买来的是『几乎没有开销』。」

那 head_dim 是不是应该改成 256,好把 TPU 喂满?

可以这么想,但别在课上把它说成建议。head_dim 是模型结构的一部分, 改它要重训,不是一个调参动作。

诚实的说法是:「如果你正在设计一个准备跑在 TPU 上的新模型, 这个数值得在设计阶段就看一眼。」 —— 对着一个已经训好的模型说「你 head_dim 选错了」,没有任何用。

⚠️ 这里我不确定的部分要讲出来:128 到 256 对模型质量有什么影响, 这门课没有数据,别顺口给结论。

3.6 那个 S 矩阵,前向不存,反向怎么办?

重算一次。反向需要它的时候,拿 Q 和 K 当场再算一遍

成本可以当场报:前向两次矩阵乘、反向四次、重算多一次 —— 七比六,多做百分之十七。 换掉的是三个数量级的搬运。

这里点到为止,不要展开讲记账口径。 MFU 和 HFU 在第 6 节有正经的一段,那里才是它的位置。

GPU 上不是也有 TMEM 吗?那不就相当于 TPU 的累加器?

是,而且这是 Blackwell 往 TPU 那个方向挪的一步 —— 值得肯定他。

TMEM 的作用是:矩阵操作数不必先落进寄存器堆整条绕过去了 —— 这跟 TPU 的累加器是同一类想法。

⭐ 而这件事的意义正好是主线的一个佐证: 「在稠密矩阵这条路上,让硬件猜的收益越来越小, 不如把控制权交回给编译器和 kernel 作者。」 —— GPU 想要这份性能,办法就是让自己更像 TPU。 第 5.4 节会正面讲这一条。

有人问「NVFP4 和 MXFP4 差在哪」「B200 硬件到底加了啥」

三十秒版本,照这三句说:

① 数值部分两个格式一模一样,都是四个 bit、只有八个可表示的绝对值。 差别全在外面那层 scale —— MXFP4 是每三十二个数共用一个、 而且只能是 2 的整数次幂;NVFP4 是每十六个数共用一个 FP8,更细也更准。

② 硬件加的是三样,没有协处理器:乘法阵列认四 bit、 MMA 通路里内嵌一级乘 scale、通用核多了几条转换指令。 硬件负责「用」scale,软件负责「算」scale。

③ 那一乘不占算力,但不等于不花钱 ——  scale 要占 TMEM(那块只有 256 KiB,一个累加器就吃掉一半), 要多走一路搬运,而量化那一头是真花时间的,只看 MMA 就会漏掉

再深就别在台上展开了,课件里那个折叠有完整的一圈流水线, 让他课后翻。

SparseCore 听起来很有用,为什么不多讲?

因为它有一道门槛,而绝大多数负载够不着。

它擅长的是同一批行被反复查很多次的情况 ——  推荐系统的嵌入表是它的主场。大语言模型这边够不着这道门槛。

但别把这句说成「语言模型用不上这颗核」 ——  用不上的只是查表那条用途,它在大模型训练里天天跑 集合通信卸载(All-Gather / Reduce-Scatter)。 正文里已经补了一整段,这个折叠只是回答「为什么不多讲查表那部分」。

那条判据的公式不要讲,也不要写进任何对外材料 ——  它的出处是内部材料。课件上只有白话释义,照着白话讲就行

别讲什么

这一讲最容易跑偏的五个方向 ① 别把上半节讲成参数导览。 那几小节全部是为 3.6 做准备 ——  每讲完一样都要落回「所以 3.6 会用到它」。 脱了这条线,十四张图就变成十四个孤立的知识点。
② 别在 3.2b 那张表上逐格念。 十行两列,念完要四分钟,而真正要的只有三个口径。 表是给他们课后回查的,现场只指那三行
③ 别在 3.3 讲 CUDA 的六层套娃。 那张完整包含关系表是折叠的,不要展开 ——  这一小节唯一的任务是「看空格」。
④ 别在 3.4 推那条错误算法。 「四千零九十六个 Tensor Core」那条错推法 折在下面,回查用。现场展开是三分钟,而它不改变任何结论。
⑤ ⛔ 别在 3.6 讲 online softmax 的递推式。 这是这一节最贵的一次跑偏 ——  推完要五分钟,而它跟两块硬件的差别毫无关系。 需要的那一句在 5.3:「分母是整行求和,不看完整行就算不出任何一个数。」
⑥ 别念任何一串「查得到的数」。这一讲折叠里攒了三批,都是回查用的: SB0SB5 和六位掩码、DSMEM 那 29/33/181/956 拍、 以及寄存器的 32 位/上限 255/按 8 取整。现场一个都不要说。
3.3 现场只要一句:「线程独占的只有寄存器,执行单元是轮着用的。」 3.3b 现场只要三样:「编译期知不知道要跑多久」这条分界线、图上中转那一列、 以及最后那句落点。
⏱ 时间口径:这一讲实测 42 分钟(2026-09-06 重量) 怎么量的:数这一节讲稿里 <p> 与「停」两类真要念出口的汉字, 除以 205 字/分(这个语速是从已讲过的 §5、§6 反推的:201 和 213)。 板书提示不计指图的时间也没算进去,所以这是个下限。
⚠️ 历次值:24 → 31 → 40 → 42。 每次上涨都是补内容:3.2b/3.2c、3.3b、然后是 2026-09-06 加的 3.2d(延迟阶梯)和 3.3c(矩阵乘的三种铺法)
加主线小节必须同时改 lec() 里那个分钟数 —— 它是全课总时长的唯一来源,没有任何检查会发现它偏小⭐ 已拍板:不砍。这门课就按实测的分钟数排 理由是内容本身没有冗余 —— 砍掉的都是有人真会追问的东西 (3.3b、3.2d、3.3c 三节全是被现场追问逼出来的)。 「60 分钟版」这个说法不再用。 真碰上只有 60 分钟的场子 —— 应急清单,顺序固定别现想 A. 3.3 和 3.5 整体折起来(−5):两小节各只有一张图, 且都是「同一个原因的第 N 次露面」,删了不断链
B. 3.2c 只留「L2 只快 2.6 倍」那一句,带宽密度那一支整段跳(−4)。
C. 3.2d 只报三个数(30 / 273 / 659)+ 那句对照,三条对策压成一句(−3)。
3.3c、3.4、3.6 任何情况下都不能砍 ——  3.3c 是 3.4 的地基,3.6 是全课高潮;砍掉它们这节课就退回成 「两块硬件参数对比」,而那正是开场第一句说了不做的事。
04

64 颗连在一起:两边连的方式不一样

⏱ 4 分钟
这一讲要留下什么 同样是 64 颗,两边连的方式不是一回事。
GB300 那 64 张卡整个装在一个 NVL72 域里(域上限 72)——  经交换机任意两点一跳,位置无关。
TPU v7 那 64 颗是 4×4×4 的三维环面 —— 没有交换机, 最远 6 跳,谁跟谁说话贵不贵取决于离多远
⚠️ 这一讲的定位 它不再是岔路,是第 6 节那组实测的前置。 不讲它,854 对 662.3 那两个数就是悬空的。

讲稿 · 64 颗的两种连法

滚到第 4 节那张「64 颗连在一起」的大图。 左右各看一眼就够,别逐格数。 开口先把这一讲的定位说掉,否则台下会以为又是一段规格: 「下一节我要摆两边的实测,六十四对六十四。 在摆之前你得先知道 —— 这六十四颗,两边压根不是一种连法。」

左边 GB300。它是一个交换式的域,域的上限是七十二颗 ——  而我们跑的是六十四,整个装得下 所以在这个规模上,任意两张卡之间经交换机一跳可达,谁跟谁说话都一样贵。

右边 TPU v7。没有交换机,每颗只连自己的邻居 ——  六十四颗排成一个四乘四乘四的三维环面每一维四个点,环绕之后最远走两步;三维加起来 —— 最远六跳

⭐⭐ 这一讲最值钱的一句,慢慢说: 「一边是位置无关,一边是位置决定成本。」 指底带那笔链路账。

总量是同一个量级:ICI 一千二,NVLink 一千八,两个都是双向合计但拆开看结论是反的 —— ICI 是六条链路每条二百,NVLink 是十八条每条一百。 单条反而是 ICI 粗一倍,NVIDIA 靠的是条数多三倍。

这里必须刹车,两条:
一,九千二百一十六除以七十二等于一百二十八 ——  这个数只衡量能铺多远,不衡量谁跑得快。 把它读成「TPU 集群快一百二十八倍」是这门课最容易犯的错。
二,torus 上的集合通信实测,这门课一次都没跑过。 所以这一讲只讲结构,不讲快慢。
⚠️ 有人问「那哪种通信模式吃亏」—— 一句话打住,别展开。 「那是『怎么切模型』的事,在专题五。这一讲只画硬件。」 展开会吃掉五分钟,而且它真的不属于这一讲。 还有一条护栏,比上面那笔链路账更要紧,一定要说「两边的『一跳』不是一个单位。」 GPU 那一跳要穿过交换机,TPU 那一跳是邻居直连 ——  1 对 6 只能读成结构差别,读不出延迟比。 滚到第 4 节第二张图(我们自己跑的实测)一分钟。

⚠️ 「拓扑能延展多远」跟「你实际拿得到多少」是两件事。 这张是我们自己实测掉了多少,不是厂商标称 ——  而且它只有 GPU 侧,同一个 NVL72 域内从 8 卡扩到 32 卡。

⭐ 这张图要带走的一句: 「『域内没有距离』不等于『域内没有代价』。」 ⛔ 同时说死:TPU 侧的对应实测这门课没跑过 ——  所以这张图不能拿来做两边对比,它只是给「一跳」祛个魅。 ⚠️ 台下常见的第三个追问:那个九百是怎么回事? 「九百是 Blackwell 的单向值,一千八是双向。而 Hopper 那一代 双向才九百 —— 两个九百指的不是一回事,常被混成一个数。」 ⚠️ 还有一条口径,问到了再说,别主动展开「『拓扑能延展多远』和『你实际拿得到多少』是两件事。」 课件里第二张图是我们自己实测掉了多少,不是厂商标称。 ⭐ 收口,接下一讲: 「记住这一句 —— 下一节摆那两个数的时候, 它们跑在两种结构不同的网上。那组数里有多少是『算得快』、 多少是『连得巧』,这门课没有拆开量过。」

可能被问到

为什么把这一节折起来?卡间通信不重要吗?

重要,但它不在这条主线上,判据很硬: 这一节的正文里 FlashAttention 出现 零次

与其让人读到这儿觉得「怎么突然跑题了」,不如在地图上就标成岔路。 —— 这一节的引子和折叠版式就是干这个的。

那 128 倍到底意味着什么?

只意味着一件事:不换协议能连到多大。 NVLink 域七十二颗、ICI 九千二百一十六颗。

超出七十二之后 GPU 那边并不是连不了 ——  是要换一套互联(走网络),性质和带宽都变了。 这才是这个数真正在说的事。

不要引申到「所以 TPU 更适合大集群」 ——  这门课没有那个规模的实测,第 6 节那唯一一组同口径实测是六十四对六十四。

完整的通信原语、NVL72 域内结构在哪儿?

L300 第 4 节,四种原语、域内结构、跨芯片寻址长什么样都在那儿。 这一版只留一个数和一句警告。

别讲什么

这一讲最容易跑偏的两个方向 ① ⛔ 别比单链带宽。 两边在同一量级,比它既没结论也占时间, 而且会把听众的注意力从「铺多远」引到「谁更快」—— 正好是这一节要防的那个错。
② 别顺势讲拓扑。 3D torus 怎么绕、NVL72 怎么接 —— 一开口就是十分钟,而这一节的预算是三分钟。
节奏提示 三分钟,而且这三分钟是可以整个不要的。
判断标准很简单:讲到第 3 节结束时看表。 如果已经超过四十五分钟直接跳过这一节 ——  把时间留给第 5 节和第 6 节,那两节是要收口的。
⚠️ 跳过的时候说一句:「卡间那一段我们跳过,课件里是折叠的,课后自己看。」 —— 别默默跳掉,课件目录里它是一个独立的大节,学员会找。 ⚠️ 没有任何东西提前告诉学员这一节是岔路,全靠你开口这一句。
05

⭐ 把那个词正式拆开:运行时,还是编译期

⏱ 12 分钟
这一讲要留下什么 两边怕的东西正好相反。
GPU 那套在「备选不够」时失效(寄存器吃紧、能同时驻留的任务变少);
TPU 那套在「算不准」时失效(形状动态、位置要跑起来才知道)。
—— 一边怕资源紧,一边怕不确定。 还有一个坑要在这一节填上 「算子融合」底下其实是两件事:不改数学的,编译器替你做; 要改数学才成立的,永远归人。 —— 不填这个坑,前面攒了三节的融合主线会在最后一步散掉。

讲稿 · 5.1 延迟归谁管

滚到第 5 节开头,停在 ms-g7。

前面每一节都停在同一个决定上,只是每次换了个部件。 这一节把那件事本身拿出来讲 ——  「谁来安排」这个决定,从硬件一路流到你写代码的那只手上。

从最底下这一层开始,因为它最能说明问题。取一次数要等几百个周期, 这段时间干什么?—— 两边的答案不是「快慢」之分,是「谁负责」之分。

GPU 的答案:一个 warp 卡住,就换下一个跑。 这张甘特图上,虚线框是「在等数据」,而别的 warp 照常在发指令。

停:「藏得住的前提是 —— 同时驻留的 warp 够多。 这就是『占用率』这个词的全部含义。」

能驻留几个由寄存器决定:每线程用三十二个寄存器,能塞满六十四个 warp; 用到一百二十八个,只剩十六个,就开始吃紧了。 —— 这就是为什么调优第一步常常是砍寄存器用量。

切到 ms-t7。这两张也是一对,要连着指。

TPU 的答案完全不同:班表在编译期就排好了,跑的时候改不了。

一个 VLIW 指令包就是这一拍要干的所有事,打成一包: 标量槽发 DMA、向量槽做归一化、矩阵槽推乘法、杂项槽放一条等待。 同一拍并发出去。

它凭什么能排这么准?因为延迟是常数。 —— 没有 cache,延迟就是常数;是常数,就能在编译期把每一拍排满。

这两句要连着说,它们是同一件事的两面: 「有 cache,延迟就不是常数;不是常数,编译期就排不了班, 于是只能准备一大批替补。
没有 cache,延迟就是常数;是常数,就能排满,于是不需要替补。」

⚠️ 但看这张图要看灰格子灰的是真的空着, 而且不会有任何东西自动顶上来 ——  编译器要么找到别的活填进去,要么就认了。

指本小节最后那个「各自会在什么时候失效」的框。 这个框是 5.1 的落点,一定要念。

这一对图真正的价值不在机制,在失效条件。
GPU 那套在「备选不够」时失效 —— 寄存器一吃紧,能驻留的少了,延迟就藏不住。
TPU 那套在「算不准」时失效 —— 形状是动态的、位置跑起来才知道, 编译期排的班就成了空班。

—— 所以两边怕的东西正好相反:一边怕资源紧,一边怕不确定。

讲稿 · 5.2 代码层

滚到 ms-p1。一分钟,不要逐行读代码。

硬件上的这个差别,会原样出现在你敲的字里。

左边 CUDA,threadIdx;右边 JAX,一个下标都没有 —— 你声明的是「这个数组按哪根轴切」,切完之后谁跑哪一块,编译器安排。

再往上一层,连集合通信也是同一个模式all-reduce 在 GPU 那边是你亲手写的第二步; TPU 那边你从头到尾没写过它 ——  它是编译器按你声明的并行方式自己插进去的。

讲稿 · 5.3 编译器的边界在哪

滚到 ms-p3。这张图是本节的中心,给两分半。

讲到这儿会有一个自然的反应:「那什么都交给编译器不就行了?」 —— 不行,而且边界画得相当清楚。

左边四样是「编排」:算子融合、通信与计算重叠、内存复用、布局与切分。 这几样它有资格自己决定。
右边是「换算法」 —— 越过这条线,它就帮不上忙了。

停,指 FlashAttention 那一格: 「它在右边,而且两边都得手写。」 —— 一个两边都失效的反例,比任何一边的成功案例都更能定义边界。

这句话把 3.6 那张对照表的意思补全了:TPU 那边少写的是搬运,不是算法。

滚到下面那个 ⭐ 框「融合底下其实是两件事」。 ⛔ 这个框是全课融合主线的收口,绝对不能跳。

你刚才可能已经绊了一下:上图左边第一格明明写着「算子融合」是编译器的领地, 落点却说 FlashAttention 编译器做不到。

停一拍再回答。「两句都对 —— 因为那是两种融合。」

第一种:不用改数学的融合,编译器替你做。 算的还是原来那些式子,只是不再走一趟冷库。 这一类你一行都不用写,两边的编译器都在默默做。

第二种:要改数学才能成立的融合,永远归人。 FlashAttention 能融,是因为先把 softmax 重写成了可以逐块增量合并的形式

—— 原来的 softmax 融不了,卡在「分母是整行求和」没看完整行就算不出任何一个数,也就没法只留一小块在灶台上。 online softmax 换的就是这一点:边走边更新最大值和分母,最后补一次缩放, 结果一模一样。

这句慢:「不是先有融合再顺便改算法, 是为了让融合成立而换了算法 ——  编译器有资格决定『怎么排』,没有资格决定『算的是什么』。」

⭐ 所以「融合由谁决定」,答案要拆成两半
能不能融、能融多大 —— 硬件说了算(3.6 那个灶台和刀宽,两个数一起卡死);
谁动手融 —— 看要不要改数学(不改的编译器做,改的只能你自己写)。

这也解释了一件乍看矛盾的事:融合是这门课里唯一贯穿始终的话题, 但它既不是纯硬件问题,也不是纯软件问题 ——  它恰好卡在两者交界上,所以研究硬件最后都会落到它上面。

讲稿 · 5.4 接住那句反驳

滚到 ms-p33。两分钟。这一段是防守,不是进攻。

讲到这里,最强的一个反驳一定会出现而且它有一半完全成立「CUDA Graph 不就是 GPU 也编译期了吗?」

先把它的成立部分说足 ——  这一段的说服力全在这儿:「整个 step 的 kernel 图提前录好、 之后一条道跑到黑,收益是真的,很多负载上开与不开是数量级的差别。」

但要判断它推翻了什么,得先讲准它究竟为什么快 —— 最常见的解释是错的。

省掉的不是「分支」,是 CPU 一条一条发 kernel 的开销。 单次启动主机端大概五到十微秒,MoE 一个 step 上万个 kernel —— 光是发就几十毫秒。开了 graph,这一整笔变成一次重放。

指图中间那一栏,那是答案。

所以问题就落在位置上了。把通路切成三层: 编排层(下一个 kernel 是什么)、落地层(块放到哪个 SM)、 发射层(下一拍执行哪条指令)。

CUDA Graph 只把第一层冻住了,后面两层一个都没动。 graph 里每个节点仍然是一次正常的 kernel 启动 ——  调度器、记分板、warp 槽、常驻上下文,全都还在工作。
而 TPU 是三层全冻,且第三层冻到具体哪个周期、哪个发射槽。

三条理由,第三条要停「硬件成本由『必须支持的最坏情况』决定,不由『你实际跑的情况』决定 ——  哪怕百分之百的负载都开 graph,3.2 里那五样部件一个都省不掉, 因为芯片流片的时候并不知道你会不会开。」

—— 所以真正值得说的是反过来那个方向:GPU 想要这份性能, 办法就是让自己更像 TPU。 而它能往前挪的只有最上面那一层,下面两层挪不动 ——  挪得动的话,那些晶体管早就省下来了。

📌 台下如果追问「那 CUDA Graph 编译出来的到底是什么、 谁在重放、重放之后记分板还在不在」—— 别临场编,课件里有个折叠专门答这四问当场展开念。 要点只有一句能背下来的:CUDA Graph 操作的是 kernel 的「外面」, 它连 kernel 里面有什么都不知道 —— 所以它在架构上就够不着后两层。

讲稿 · 5.4b 第二句反驳:那 TMA 呢

滚到 5.4b。两分钟。⭐ 这一问比 CUDA Graph 那句更值得答。

还有第二句反驳,而且它比上一句强得多 —— 因为它说的方向是对的「搬运这条路,GPU 不是也在往 TPU 那边挪吗?」

先痛快承认。这一段的说服力全在「先认下来」这四个字上。 「对,而且趋势藏不住。把 3.3b 那张四代图竖着读一遍就看见了。」

一直都有的:普通取数 —— 数据在寄存器里过一道, 地址、跨步、边界全靠线程自己算,而且搬完才轮到它算。
Ampere 起:砍掉寄存器那一道,地址仍然自己算
Hopper 起:TMA —— 一个线程填一张描述符发出去就走, 跨步、偏移、边界整个由硬件接管。 三十二条 lane 各算一个地址这件事,在这条路上主动放弃了
Blackwell 起:这一步最狠 —— 矩阵指令由单个线程代表整个 CTA 发射, 操作数和累加器搬进专用矩阵暂存,连结果都不回寄存器堆

停一拍,把终点说出来: 「四代下来,那条通路的终点长这样 ——  整块搬、按描述符搬、不占线程、落在一块软件说了算的专用暂存里。」
「—— 而这正是 TPU 第一天的样子。」

但这条趋势线只走了一半,剩下那一半才是这门课的主线。

收敛的是「怎么搬」:粒度、寻址的主体、操作数的落点 ——  这三样确实在往同一个终点走。
没收敛的是「谁决定」,而这一层一步都没动TMA 那张描述符是运行时建的,不是编译期烤进二进制的; L1、L2 还在,命中不命中照样跑起来才知道;warp 调度器照样每个周期现挑。

⭐⭐ 这一节的落点,念慢: 「准确的说法不是『GPU 在变成 TPU』,是『GPU 把喂料这件事 做成了 TPU 的样子,而谁来发号令那一层一动没动』。 两边的分歧从来不在终点,在谁来发这条 DMA。」

⚠️ 最后补一句为什么这条趋势必然发生,别让人以为是抄袭: 矩阵单元越快,「喂料」这件事就越不能占着算的人。 一条矩阵指令几拍里吃掉几十 KB,你还让三十二个线程轮流去算地址, 那些线程就成了自己算力的看门人
—— 两家面对的是同一道物理题,于是给出了同一个答案 —— 只不过一家走了四代,另一家出厂就在终点。

可能被问到

⭐ 编译器既然能做融合,为什么最有名的那个融合它反倒做不到?

这是这一节最该被问的问题,而且课件里专门有一个框回答它 ——  不要临场编,翻到那个框念。

因为「融合」底下是两件性质完全不同的事。
不改数学的:算的还是原来那些式子,只是中间结果不落 HBM。 这一类编译器默默就做了。
要改数学的:FlashAttention 属于这一类。 原来的 softmax 分母是整行求和,不看完整行算不出任何一个数 —— 所以它根本没法只留一小块在片上。 必须先把算法换成 online softmax,融合才成立。

落点一句:编译器有资格决定「怎么排」,没有资格决定「算的是什么」。

那 Triton 和 Pallas 算什么?它们不是编译器吗?

它们是「越过边界之后你拿什么写」的答案,定位几乎一样。 共同点是:你不用自己写 load 和 store。

但注意它们出现的位置 —— 在边界的右边需要它们,恰恰说明这件事编译器自动做不了。

别在这儿比 Triton 和 Pallas 谁好用 ——  这门课里那属于生态差距,而生态会随时间收敛,架构不会第 9 节收尾那三条限定里,第 ② 条说的就是这件事。

「FLOPs 一分不省」是不是说 online softmax 完全没有额外开销?

不是,这里要主动按住一个常见误读。

3.6 说的「一分不省」指的是两次矩阵乘的量online softmax 每块要多做一次缩放 ——  但那是 O(n·d),和 O(n²·d) 比可以忽略。

「不改数学」说的是结果等价,不是「一条指令都没多」。

TPU 编译慢是不是个大问题?

是问题,但它的性质要说清楚 —— 它是「成本挪了位置」,不是「多出来的成本」。

GPU 那边同一件事是每次运行时由调度器现做的成本摊在了跑的时候;TPU 把它一次性付在编译期。

⭐ 顺带说一句它换来了什么:跑起来非常稳。 同一份 GPU 代码两次跑的耗时可以差很多,TPU 这边基本不会。

⚠️ 形状一变就要重编,这一条是真痛点 ——  完整的移植账单在 L300 5.5,这里别展开。

如果我全程都开 CUDA Graph,那 GPU 那五样部件是不是就白养了?

这个问题问得很好,而且答案是这一节最硬的一条:白养也得养。

硬件成本由「必须支持的最坏情况」决定,不由「你实际跑的情况」决定。 —— 芯片流片的时候,并不知道你会不会开 graph。

而且就算开了,graph 只冻住了最上面那一层: 块落在哪个 SM、下一拍发哪条指令,还是运行时决定的调度器和 warp 槽照常在工作。

别讲什么

这一讲最容易跑偏的四个方向 ① 别逐格讲那两张甘特图。 它们是示意,不是实测 trace(图上写了)。 要的只有一句:一边靠换人,一边靠排班
② 别逐行读 5.2 那两段代码。 要的是「一边有 threadIdx, 一边一个下标都没有」—— 读完两个下标就走。
③ ⛔ 别在 5.4 变成给 CUDA Graph 挑刺。 这一段的说服力 全在「先把它的成立部分说足」。一上来就找它的毛病, 台下懂 CUDA 的那位会立刻不信你后面所有话。
④ 别展开讲 XLA 的 pass。 「编译器能做哪四样」那张图已经够了 —— 讲 pass 是编译原理课,不是这门课。
节奏提示 九分钟,五张图,但重心明确压在 5.3。
5.1 三分钟(两张图,落点是「失效条件正好相反」)、 5.2 一分钟5.3 三分半、5.4 一分半
5.3 那个「两种融合」的框是全课融合主线的收口 ——  §1 起名、§2 演示、§3.6 落地、到这儿才算讲完时间再紧也不能跳它;真要砍,砍 5.4。
⚠️ 5.4 有一个特殊风险:它会招来现场最懂 CUDA 的那个人。 答法在「可能被问到」里写全了 ——  照着答,答完就走,别顺势开辩论。
06

跑完之后:拿到两个数,能不能比

⏱ 8 分钟
这一讲要留下什么 ⛔ 不是那个 1.31 倍。 如果学员只记住了「GB300 快 1.31 倍」,这一讲就是失败的。
要留下的是这一句:这个数往哪边挪都有理由,而我们两个方向都没量过。
以及四句能带出门的问法 这个数是算出来的吗 · 二进制还是十进制 · per chip 还是 per device · 物理量还是可用量。 换成别的两块硬件,这四句照样用 —— 这才是这门课真正的产出。

讲稿 · 6.1 先把「怎么比」定下来

滚到第 6 节开头,停在 ms-p14 那张四档图。

六站走完了,两边各有一个数。但在把它们摆上桌之前, 得先说清楚「怎么比才算数」。

最常见的两种比法都不对。
比峰值 —— 那是芯片能干什么,不是你的活跑多快
比 MFU —— 分母是自己选的,换个分母就换个结论。

—— 剩下唯一站得住的,是这张图上的第 ③ 档同一个负载,两边各真跑一次。

⚠️ 图上还有个 ④,每美元、每瓦 ——  它才是采购真正要的答案,但它买不到捷径④ 的分子就是 ③,没有 ③ 的实测,④ 只是把两个不确定的数相除。

展开下面那个折叠「这门课自己就栽过一次」。 ⭐ 这一段是这一小节的全部说服力,一定要讲,而且要讲得平静。

「换个分母就换个结论」不是吓唬人。我们自己栽过一次。

同一次运行、同一个分子 854.0,我们先后报出过两个 MFU
854 除以 2,700,等于 31.6% —— 写进仓库,挂了二十六天。
854 除以 2,500,等于 34.16% —— 八月三十一号更正。

停一拍:「差两点六个百分点 ——  而这中间没有任何一次重跑,实测数一个字都没变。」

错在哪:那个 2,700 是推出来的,不是查出来的。 写它的人拿 GB200 的 2,500 乘了个一点二,理由是「Ultra 总该快点吧」。 —— 但官方从来没有 2,700 这个数。 GB300 的 dense BF16 峰值与 GB200 完全相同; Ultra 提升的是 FP4 和 attention,不是 BF16。

⭐ 落点在这儿,要慢「这一笔最值得带走的不是『要细心』,是它为什么难被发现 ——  2,700 长得完全像一个官方规格。量纲对、数量级对、比 GB200 大一点也符合直觉, 没有任何一处会报错。」

而 31.6% 这个下游数字更隐蔽它是老老实实算出来的,算式一点毛病没有。

—— 所以「这数是算出来的吗」这一问,问的不是你,是你引用的那个数。

讲稿 · 6.2 ⭐ 分子那一侧:MFU 与 HFU

这一小节没有图,只有一个框、一张表、两个框。 换气点全靠语速,讲的时候注意别连着四屏黄底。

上面那张入场券问的全是分母该拿哪个峰值。但分子也有两种取法 —— 而且这两种都有名字、都在被人用。

区别只有一句话:分子是「模型数学上需要多少次运算」, 还是「硬件实际执行了多少次运算」。

🍳 先给画面,还是那把刀(这个比方 3.6 已经立好了, 这里只是从「刀多宽」延到「挥了多少刀」,不要另造新比方):

分母一样 —— 这块硬件一秒最多能挥多少刀。
MFU 的分子等于菜谱上一共需要多少刀。
HFU 的分子等于你实际挥了多少刀 —— 包括切废了重来的、切空气凑数的。

—— 所以 HFU 永远大于等于 MFU,差的就是白挥的那些刀。

该看哪个,取决于你在问什么
「这把刀有没有闲着」→ 看 HFU。
「这顿饭多久能好、要花多少钱」→ 看 MFU —— 白挥的刀不会让饭早一秒好。

滚到 ms-p13「MFU 是一个分数」⭐ 这张图是 6.2 的主图,给足时间。先只看甲格

甲格三条,是分子的三个选择 ——  第一条就是刚才那次翻车(算力公式漏了白名单,约五倍); 第二条正是我们在讲的 HFU 对 MFU;第三条看序列长度。

第二条,能差多少?先拿本课这个算子算:FlashAttention 反向要重算一次 前向的 S,整层七次矩阵乘里有一次是白做的 ——  七比六,约一点一六七倍,有用的占百分之八十五点七。

图上写的四比三是上界 —— 整层重算,中间结果一个不存、 反向时把前向整个重做一遍。

移到中格这一格是全图重心。

同一次 run、实测 503,一行代码没改,能合法报出四个百分比二十二点四、二十点一、十一点二、十点一。 沿着行走是换精度口径,沿着列走是换 SKU。

⭐ 这句必须说出口:「这四个格子只动了分母 ——  甲格那三个分子选择一次都没动。所以两点二倍是下限,不是上限。」

乙格四条是分母的选择每一条单独看都有官方依据 ——  难的从来不是找一个,是说清用了哪一个。

这张图唯一要带走的动作在最底下那条蓝带「报 MFU 的时候,把这一整行一起报出来。」 写不出这一行,就别报百分比 —— 退一步报吞吐本身。 ⭐ 这句是这一段的价值所在: 「两个数都是从定义推出来的,不是测出来的 ——  而它们相差一倍,正说明『开了重算』这四个字本身说明不了任何事。」 滚到「屋顶线该用哪个分子」那个框。

⚠️ 还有一条必须说,它是这一节和第 1 节之间的接口问「我会不会撞到屋顶」,必须用 HFU 那一侧的分子 —— 也就是含重算、含补零的那个数。

道理很硬:那些重算出来的运算,是真的要占矩阵单元周期的 —— 硬件不认识哪些是重算。

一句话记法:判在哪一侧用 HFU 那个,报产出用 MFU 那个。

课件里有个当场能算的例子,值得念,它让这条抽象规则落地「假设一个算子里有 30% 的运算是重算 ——  有用强度才刚到 219,含重算的强度就已经顶在 312 上了。 也就是说:按 MFU 那个分子看,你离屋顶还远;按 HFU 看,你已经贴上去了。」
⛔ 顺带说死方向:「用错了分子,错的方向是单向的 —— 它只会让你低估。」
最后那个黄框,一条,十五秒

比两块硬件之前,先问一句「重算策略一样吗」。 一边全关、一边开着,HFU 可能很接近而 MFU 差一大截,反过来也成立。 —— 6.3 末尾那张「还缺什么」图的第 ③ 条说的就是这个, 这里不铺垫,那儿就讲不通。

讲稿 · 6.3 两边的实测

滚到 ms-p15。⚠️ 讲这张图有个诀窍: 先别看那两个大数,先指下面那两段红色。

入场券拿了,摆数。

顺序千万别反。先指两段红色: 「这两段一样长。」停一拍,说上面那两个大数。

GB300 六十四卡:854.0。TPU v7 六十四芯片:662.3。 看上去差很多 —— 但下面那两段红色一样长。 实测差 191.7,峰值差 193.0。

—— 差的是分母,不是本事。

把这 1.29 倍拆开,只拆得动一半峰值之比 1.084(2,500 除以 2,307),这是能算清的那段; 剩下 1.190 是余项 —— 它不等于「软件」, 里面还有收缩维形状、互联拓扑这些。

滚到 ms-p18。⭐ 这张是这一节真正的产出。

最难的一步在后面:不把这个数说过头。 这个 1.31 倍还缺什么?—— 先让他们自己列一遍,再对答案。

四条红,两条绿。
推高的(利于 GB300)四条:global batch 不一样、六十四这个规模是 GPU 的主场、 重算策略不是同一件事、两边软件成熟度差很多。
压低的(利于 v7)两条:662.3 含一个频率档、「每芯片对每 GPU」已经是最优待。

这一节的落点,一字不改地念「红绿是反向的 —— 也就是说,这个数往哪边挪都有理由, 而我们两个方向都没量过。 一份负责任的材料到这一步该做的不是把数字说圆,是把没量的部分列出来。」

—— 所以这一节能带走的是那四句问法这个数是算出来的吗 · 二进制还是十进制 · per chip 还是 per device · 物理量还是可用量。 换成别的两块硬件,这四句照样用。

可能被问到

所以到底是 GB300 快,还是 TPU 快?

这个问题一定会被问,而且是这一节的试金石 —— 别顺着答。

可以说的只有一句:「在这一个模型、这一个规模、 这一次配置下,GB300 每芯片快 1.31 倍。」

然后立刻补第二句:「而这个数的六条修正里, 四条推高、两条压低,两个方向我们都没量过 —— 所以它是一个点,不是一个界。

不要说「所以 GPU 更快」,也不要辩解「但 TPU 便宜」 —— 后者是第 ④ 档的问题,这门课没有那组数。

⭐ 你们自己那个 2,700 的错,为什么要在课上讲出来?

因为它是全场最有说服力的一个例子,而且它是我们自己的。

它同时证明三件事: 分母真的会动; 动的幅度足以改变结论(两点六个百分点); 出事的方式不是算错,是把一个推出来的数当成了官方数

但讲的时候不要讲成「我们很严谨」的自夸。 落点是那条可迁移的判据:看到一个「长得像官方规格」的数, 先问它有没有出处。

MFU 和 HFU,框架报的是哪个?

主流框架报的基本都是 MFU 口径 ——  算力公式按模型数学算,不含重算

⚠️ 所以拿框架报的那个数直接往第 1 节那条线上套,会低估。 判「会不会撞屋顶」要用含重算的那个。

⭐ 还有一句实用的:比两块硬件之前,先问「重算策略一样吗」。 两边一边全关重算、一边开着,HFU 可能很接近,而 MFU 差一大截。 —— P-18 第 ③ 条说的正是这件事。

为什么用「每芯片」比,不用「每卡」或者「每机架」?

因为它已经是对 TPU 最优待的口径了,这一点要主动说破。

v7 是 两个 device 一颗 chip我们选了每芯片对每 GPU, 也就是拿「两个 die」去对「一整张卡」—— 这是 P-18 里那两条 「利于 v7」中的一条。

反过来按 device 比才是对 v7 最难看的:662.3 ÷ 2 = 331, 倍数从 1.31 变成 2.6

⚠️ 「per chip / per device / per core」正是那四句问法里的第三句。 被问到,就是这句问法在现场兑现了一次。

1.29 和 1.31,为什么两个数?

单位不一样,而且这个差值本身就是这一节的教材。

1.29 是每芯片 TFLOP/s(854.0 除以 662.3), 1.31 是每芯片 token/s(6,242 除以 4,770)。

—— 换一个单位,倍数就变了,变掉的正好是那 1.5%。 这就是「同一件事,报法不同结论不同」的现场版。

别讲什么

这一讲最容易跑偏的四个方向 ① ⛔ 别把 1.31 讲成结论。 这是这一节唯一致命的讲错 ——  一旦学员记住的是这个倍数,前面五节全白讲了它是一个点,不是一个界。
② 别在 6.2 展开讲怎么算 FLOP 公式。 分子分母各怎么数、 要不要算白名单 —— 那是另一门课。这里只要「两种取法、各答什么问题」。
③ 别辩护。 有人说「TPU 慢」的时候,不要去找理由 —— 直接把 P-18 那六条推给他看,让表说话。
④ 别顺势聊性价比。 那是图上第 ④ 档,这门课没有那组数。 要聊就明说:「④ 的分子就是 ③,我们只有 ③。」
节奏提示 九分钟,三张图 —— 但 6.2 没有图,它是这一节最容易失控的地方。
6.1 三分钟(图一分钟,那个 2,700 的红框两分钟,值得)、 6.2 三分钟、6.3 三分钟
⚠️ 6.2 连着一个框、一张表、两个框,全是文字,没有换气点。 讲法上要自己造停顿:🍳 那个刀的比方讲完停一次, 七比六和四比三报完再停一次否则台下会在第三个框的位置走神。
6.3 那张图的讲述顺序是硬的:先指两段红色,再报两个大数。 反过来讲,这张图的全部意义就没了 ——  先看到 854 和 662,人的第一反应一定是「差 200」,后面说什么都拉不回来。
09

那到底各自擅长什么

⏱ 5 分钟
这一讲要留下什么 一句话,加三条限定。缺一不可。
「两边的差别不是『谁更强』,而是『把控制权交给谁』。」
—— 只带这一句出门,它就只是一个立场。 三条限定才是这门课挣来的。 ⚠️ 这一节最容易被单独截图转走 所以结论和边界必须同框 ——  课件上每一行的出处标号都写在同一行里,就是为了这个。 ⛔ 讲之前先翻教材 这一节只有 9.1 一个小节、一张图(P-24)。 2026-09-06 之前的旧稿里那张「八行取舍表」已经删了 —— 别再讲它。

讲稿 · 9.1 落点

滚到第 9 节,停在 P-24全课就剩这一张图,四分钟。

从封装拆到实测,两块硬件走完了一整趟。开场我说「处处都不同,但只有一个成因」, 3.6 把全程摊开数过,六行里四行两边一样 ——  现在可以说那个成因到底是什么了。

先指图中间那个提问框。那是全课的骨架。

整门课只有一个决定要问:运行时才知道的那些事 ——  形状、访问落在哪、谁忙谁闲 —— 交给谁处理?

GPU 那一侧:交给硬件在运行时处理,也就是交给了人。
TPU 那一侧:交给编译器在编译期算准,也就是接受它的脾气。

⚠️ 指红绿两栏时立刻补一句,别等人误会「红绿只标是哪一侧,不含好坏 ——  而且这门课唯一那组两边实测,赢的恰好是红那一侧。」 横着念左边那三行。一行一句,别概括。

① 同一次访存,路上要做几个决定? —— GPU 主路四个,TPU 零个。 命中不中要跑起来才知道,对面是全在编译期定死、走哪条路是算出来的。

② 所以代价出现在什么时候? —— GPU 每一次访存都付一点,藏在运行时里,看不见也不用管; TPU 一次付清,但付在你身上。

第 ② 行有实数,一定要报出来 ——  这是全课唯一一处把「静态税」量化了的地方「冷启动十九分钟编译,热启动仍要七分半, 其中十六次是失败的。重编每次都白付 ——  而且还得有人会写 Pallas。」

③ 那前提不成立的时候怎么办? —— GPU 运行时还能换一条路,硬件自己会绕; TPU 运行时无处补救,只能回去改代码,重来一遍编译。

指图右格那四句。这是唯一能带出门的东西。

四句问法,跟这两块硬件无关 —— 换成别的两块照样用。 课件上每一句后面都带着具体的坑,念的时候带上,不然它只是四个口号:

一,这个数是算出来的吗?是的话,算式写在它旁边了吗
二,二进制还是十进制?官方 HBM 那栏写的是 GiB ——  192 GiB 等于 206 GB,差 7.4%
三,per chip、per device、还是 per core? v7 是 2 device 一个 chip,而日志按 device 报 ——  这一步错,结论差一倍
四,物理量还是可用量?94.74 GiB 是可用的; 2,500 是 dense,不含稀疏。

全课的落点。这句要慢,要停,念完不要马上接下一句。 「两边的差别不是『谁更强』,而是『把控制权交给谁』。 交给人,就得有人有能力写;交给编译器,就得接受它的脾气。」 ⛔ 指图底那三条限定。一条都不能省。 如果时间只够念一样,念这三条,别念上面那句结论 ——  结论他们已经听了一个半小时了。

唯一那组两边同口径的实测,就是 GB300 快 1.31 倍 —— 它是一个点,不是一个界。
「TPU 要手写 kernel」是生态差距,不是架构差距 —— 生态会随时间收敛,架构不会。混成一件事讲, 就等于在说一个不会变的劣势。
机制上 TPU 那条路线的前提在规整负载上成立, 但这门课从来没有用数据兑现过它 —— 这一条是我们自己欠的。

如果嫌「把控制权交给谁」太抽象,就给他们一个抓手「只带一样东西回去的话,带算子融合。」 那张图讲的是原则,融合是这门课里唯一从头贯到尾的具体实例 —— 而它恰好把那条原则的三层都走了一遍。

为什么要融:不改数学就能往屋脊线右边挪,只有这一招
能融多大:工作台多大加刀多宽,硬件两个数一起卡死
谁动手融:不改数学的编译器替你做,改数学的永远归人

—— 物理、硬件、控制权,三层各卡一道。 这就是为什么研究硬件,最后总会落到融合上。

最后一句,念完就结束,不要再加总结「这门课没有告诉你该买哪个。它想让你下次看到一份 『A 比 B 快 N 倍』的材料时,知道该问哪四句话, 以及翻到第几页去找它的分母和分子。」
⭐ 如果还剩十秒,补一句回扣: 「6.2 那两个都叫『利用率』的数,分母一样,差的全在分子。」