🏠 总目录📚 本教程 Roofline 与 MFU
📑 本页目录(点开跳转)

04 · Roofline 与 MFU

28 分钟 | ⭐⭐ 唯一能回答"我还有多少空间"的工具


🎯 一句话

不会算 MFU,你的所有优化都是盲调。 这一章给你两个工具:Roofline 告诉你瓶颈在哪,MFU 告诉你离天花板还有多远。

平衡点A100 ≈ 156DecodePrefill / 训练带宽受限(搬不过来)算力受限(算不过来)算术强度 = 每读 1 字节做多少次浮点运算 →实际算力 ↑一条折线,就能判断你被什么卡住了⭐ Decode 天然落在左边斜坡上 —— 它的 MFU 只有 1~5% 是【物理决定的】,不是配置错了所以推理优化的方向是「提高算术强度」(批处理、KV Cache),而不是买更强的算力
左边斜坡是带宽受限(搬不过来),右边平台是算力受限(算不过来)。⭐ Decode 天然落在左边 —— 它的 MFU 只有 1~5% 是物理决定的,不是配置错了。所以推理优化的方向是「提高算术强度」,不是买更强的算力。

📈 一、Roofline 模型

横轴是算术强度(FLOP/字节),纵轴是性能(TFLOPS)。曲线由两段拼成:左边一段斜坡斜率 = 显存带宽,这是带宽上限),右边一段水平屋顶(算力上限,A100 是 312 TFLOPS)。两段的交点就是拐点,A100 落在 156 FLOP/字节。你的每个 kernel 都会落在这张图的某个位置上:

你的 kernel 落在哪 说明 该优化什么
斜坡上 带宽瓶颈(算术强度不到 156) 优化【搬运】
屋顶下方 算力瓶颈 优化【计算】或用 Tensor Core
屋顶上 超过了硬件峰值 你算错了 😄

$$\text{可达性能} = \min(\text{峰值算力},\; \text{算术强度} \times \text{带宽})$$

💡 人话要么被算力卡住,要么被带宽卡住,取小的那个。

🔨 三步用起来

   ① 算这个 kernel 的 FLOP 和字节数 → 得到算术强度
   ② 在图上找到它的位置 → 知道理论上限是多少
   ③ 和实测对比:
      · 接近理论上限   → 这个 kernel 已经到头了,去优化别的
      · 远低于理论上限 → 有 bug 或实现差,值得深挖 ⭐

🔑 Roofline 最大的价值不是"告诉你能快多少", 而是"告诉你【不能】快多少" —— 一个算术强度只有 2 的 kernel,无论你怎么优化计算,都不可能超过 4 TFLOPS。 知道这个,你就不会在错误的地方浪费一周。


📊 二、MFU:最重要的一个指标

$$\text{MFU} = \frac{\text{实际有效算力}}{\text{硬件峰值算力}}$$

怎么算(这是你真正会用到的)

   ① 算出一次前向+反向的 FLOP:

      ⭐ Transformer 的经验公式:
         每个 token 的训练 FLOP ≈ 6 × 参数量

         (前向 2N,反向 4N —— 反向要算两组梯度:
           对输入的和对权重的,所以是前向的 2 倍)

   ② 实测吞吐(tokens/秒)

   ③ MFU = (6 × N × tokens/秒) / (GPU数 × 峰值FLOPS)
def mfu(params_B, tokens_per_sec, n_gpus, peak_tflops=312):
    """Transformer 训练的 MFU。peak_tflops: A100 BF16 = 312"""
    flops_per_token = 6 * params_B * 1e9
    achieved = flops_per_token * tokens_per_sec
    peak = n_gpus * peak_tflops * 1e12
    return achieved / peak

# 例:7B 模型,8 卡 A100,实测 12000 tokens/秒
print(f"MFU = {mfu(7, 12000, 8):.1%}")     # MFU = 20.2%   ⚠️ 偏低,有优化空间
📐 为什么是 6N(想看再点)

对一个权重矩阵 $W \in \mathbb{R}^{m\times n}$,处理一个 token:

合计 $6mn$,而 $mn$ 就是这个矩阵的参数量 → 6 × 参数量

⚠️ 这个公式忽略了 attention 的 $O(n^2)$ 部分。 序列很长时要加上:$\text{每 token} \approx 6N + 12 \cdot L \cdot h \cdot s$ (L=层数,h=hidden,s=序列长度)。 序列 ≥ 8K 时这一项不能忽略。

📏 MFU 该是多少(对照基准)

MFU 评价 该干什么
< 20% 💀 有明显问题 先查数据管线,多半是没喂饱
20–35% ⚠️ 偏低 查激活检查点配置、通信、算子融合
35–45% 🟡 一般 常规优化还有空间
45–55% 良好 已经不错了
55–60% ⭐ 优秀 接近天花板
> 60% 🤔 检查你的公式 可能算错了(或用了 FP8)

🔑 为什么天花板是 60% 而不是 100%: 存在无法消除的开销 —— 通信、非矩阵乘操作(LayerNorm/Softmax/激活函数)、 数据加载、kernel 启动、优化器更新。这些都不算进"有效 FLOP"但要占时间。


🔍 三、MFU 低了怎么查(按顺序)

   ① GPU 有没有在跑?        → nvidia-smi 看利用率,长期低于 90% = 没喂饱
   ② 数据管线跟得上吗?      → 把 dataloader 换成随机张量,MFU 变了吗?⭐
   ③ 通信占多少?            → 单卡 vs 多卡的 MFU 差多少
   ④ 非矩阵乘操作占多少?    → profiler 看 kernel 时间分布
   ⑤ 有没有用上 Tensor Core?→ 精度对不对、维度对齐了吗
   ⑥ 激活检查点是不是开太狠?→ 全量重算会掉 30% 以上

第 ② 步是最值得先做的诊断

# 把真实数据换成固定的随机张量,其他不变
batch = torch.randint(0, vocab, (B, S), device='cuda')
# 如果 MFU 显著上升 → 瓶颈在数据管线,不在模型(第 22 章)

这一个实验能在 5 分钟内排除掉一整类问题。

🔬 用 PyTorch Profiler 找热点

from torch.profiler import profile, ProfilerActivity, schedule

with profile(
    activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
    schedule=schedule(wait=1, warmup=2, active=3),   # ⭐ 跳过预热
    record_shapes=True, profile_memory=True, with_stack=True,
) as prof:
    for _ in range(6):
        train_step()
        prof.step()

print(prof.key_averages().table(
    sort_by="self_cuda_time_total", row_limit=20))   # ⭐ 看谁最耗时
prof.export_chrome_trace("trace.json")               # 拖进 chrome://tracing

读 trace 的三个信号

你看到 说明
GPU 时间轴上有空隙 GPU 在等 CPU 或数据 → 数据管线 / CPU 开销
一堆小 kernel 密集排列 没有融合,kernel 启动开销占比高 → 第 7 章
通信 kernel(nccl)占比大 通信瓶颈 → 检查拓扑或换并行策略

💰 四、另外两个你该会算的数

① 训练一个模型要多久

$$\text{训练时间} \approx \frac{6 \times N \times D}{\text{GPU数} \times \text{峰值FLOPS} \times \text{MFU}}$$

(N = 参数量,D = 训练 token 数)

   例:7B 模型,1T tokens,64 张 A100,MFU 45%

   6 × 7e9 × 1e12 / (64 × 312e12 × 0.45)
   = 4.2e22 / 8.98e15
   ≈ 4.7e6 秒 ≈ 54 天

   ⭐ 如果 MFU 只有 25%:97 天 —— 差了 43 天的机时

💡 这个公式的实用价值在开始训练之前就能估算成本。 MFU 从 25% 提到 45%,在这个例子里省下的机时价值几十万元。

② 推理成本

   每个输出 token 的 FLOP ≈ 2 × 参数量   (只有前向)

   ⚠️ 但推理是【带宽瓶颈】,用 FLOP 算会严重低估
   ⭐ 正确的估法:每生成 1 个 token 至少要把【全部权重读一遍】

   → 理论最快解码速度 = 显存带宽 / 模型大小

   例:7B 模型 BF16(14GB),A100(2TB/s)
       2000 / 14 ≈ 143 tokens/秒   ← 单个请求的理论上限 ⭐

🔑 这个"带宽除以模型大小"是推理领域最有用的心算公式: 它解释了为什么量化能直接提速(模型小一半 → 速度快一倍), 也解释了为什么批处理是免费的(读一遍权重服务多个请求)。 🔗 第 151618 章全建立在这上面。


🔗 和站内其他章的关系

相关的地方 这里的位置
第 3 章 算术强度 Roofline 的横轴
第 3 章 平衡点 156 Roofline 的拐点
第 1 章 先测再优化 本章就是"怎么测"
第 2 章 GPU-Util 有误导性 MFU 才是真指标
《机器学习与深度学习基础》11 训练调试手册 「GPU 跑不满」 那里只能定性说"利用率忽高忽低",MFU 把它变成一个能横向比较的数字
《大模型全景导论》06 推理优化 量化 / 批处理 / 投机解码 那三件武器的共同依据,就是本章末尾的"带宽 ÷ 模型大小"
《模型上线之后》18 成本与容量 本章算的是机时;折成"一次请求多少钱、峰值要几台机器"在那边

✅ 检查点

  1. Roofline 模型的两条线分别代表什么?拐点在哪?
  2. Roofline 最大的价值是什么?(不是"能快多少")
  3. MFU 怎么算?Transformer 每 token 的训练 FLOP 是多少?
  4. 为什么是 6N?这个公式什么时候会失准?
  5. MFU 多少算良好?为什么天花板是 60% 而不是 100%?
  6. MFU 低了,最值得先做的一个诊断实验是什么?
  7. 读 profiler trace 时,"GPU 时间轴有空隙"说明什么?
  8. 推理领域最有用的心算公式是什么?它解释了哪两件事?
👀 答案
  1. 斜坡是带宽上限(斜率=显存带宽),屋顶是算力上限。拐点在算术强度 = 峰值算力/带宽,A100 上是 156 FLOP/字节
  2. 告诉你"不能"快多少。一个算术强度只有 2 的 kernel,无论怎么优化计算都超不过 4 TFLOPS——知道这个就不会在错误的地方浪费一周。
  3. MFU = (6 × 参数量 × tokens/秒) / (GPU数 × 峰值FLOPS)。每 token 训练 FLOP ≈ 6 × 参数量
  4. 前向 2mn + 反向对输入 2mn + 反向对权重 2mn = 6mn,而 mn 就是参数量。忽略了 attention 的 O(n²) 部分,序列 ≥ 8K 时会失准,要加 12·L·h·s。
  5. 45–55% 良好,55–60% 优秀。天花板不是 100% 是因为存在无法消除的开销:通信、非矩阵乘操作(LayerNorm/Softmax)、数据加载、kernel 启动、优化器更新——占时间但不算有效 FLOP。
  6. 把 dataloader 换成固定的随机张量,其他不变。如果 MFU 显著上升 → 瓶颈在数据管线不在模型。5 分钟排除一整类问题
  7. GPU 在等 CPU 或等数据 → 数据管线或 CPU 开销有问题。
  8. 理论最快解码速度 = 显存带宽 ÷ 模型大小(7B BF16 在 A100 上 ≈ 143 tokens/秒)。解释了:量化能直接提速(模型小一半速度快一倍)、批处理是免费的(读一遍权重服务多个请求)。

🛑 可以停在这里

走神救援

⭐⭐不会算 MFU,所有优化都是盲调Roofline:可达性能 = min(峰值算力, 算术强度×带宽);斜坡=带宽上限、屋顶=算力上限、拐点在 156(A100)。⭐它最大的价值是告诉你"不能"快多少——算术强度 2 的 kernel 怎么优化都超不过 4 TFLOPS,知道这个就不会浪费一周。⭐⭐MFU = (6×参数量×tokens/秒) / (GPU数×峰值FLOPS)每 token 训练 FLOP ≈ 6N(前向2N + 反向对输入2N + 反向对权重2N),⚠️忽略了 attention 的 O(n²),序列≥8K 时要加 12·L·h·s基准:<20% 有明显问题、20-35% 偏低、45-55% 良好、55-60% 优秀、>60% 检查公式;⭐天花板是 60% 不是 100%,因为通信/LayerNorm/Softmax/数据加载/kernel启动/优化器更新占时间但不算有效 FLOPMFU 低了按序查:GPU在跑吗 → ⭐把 dataloader 换成随机张量(5 分钟排除一整类问题) → 通信占比 → 非矩阵乘占比 → Tensor Core 用上没(精度和维度对齐)→ 检查点开太狠。读 trace 三信号时间轴有空隙=在等CPU/数据、一堆小 kernel=没融合、nccl 占比大=通信瓶颈。训练时间 ≈ 6ND / (GPU数×峰值×MFU)(7B/1T tokens/64卡/MFU45% ≈ 54 天,MFU 25% 则 97 天)。⭐⭐推理最有用的心算:理论最快解码速度 = 显存带宽 ÷ 模型大小(7B BF16 在 A100 上 ≈ 143 tokens/秒)——它解释了量化为什么直接提速批处理为什么是免费的

下一节 👉 05-互联与集群拓扑.md

打卡记录保存在你的浏览器里,首页能看到总进度