Skip to the content.

峰值算力怎么算,FLOP 怎么数

读 benchmark 数据时最容易出错的地方是”峰值”这个词:它是算出来的,不是测出来的,而且算的时候用了几个不可查询的假设。这篇用 my-result.md 里 2026-09-22 的 GPU 数据当例子,把计数约定、公式来源、利用率的含义拆开。

一个 FLOP 在这里怎么数

GPU 世界里峰值一律按 一次 FMA = 2 个 FLOP 数(一次乘法 + 一次加法)。被测指令是:

fma.rn.ftz.f32 x, x, a, b     // x = x * a + b

这条指令只占 1 个发射槽、1 个 CUDA core 的 1 拍,但行业约定算它 2 FLOP。所有厂商标的 TFLOPS 都用这个约定,所以横向对比时不用换算;代价是”25.4 Tflop/s”里的 flop 数是逻辑运算数,不是指令数 —— 换成指令数要除 2,即 12.7 G 条 FMA/秒。

整数那边同理:一次 mad.lo.s32 算 2 个 op,所以 INT8 的峰值报的是 TOPS 而不是 TFLOPS,数法一样。

测的是什么

fma_f32<8>:每个线程维护 8 条互相独立的累加链,每条循环一次做一次 FMA。8 条独立是为了把 FMA 流水线的延迟完全藏起来 —— 如果只有 1 条依赖链(同套件的 chain1),每做一次就得等上一次的结果,实测只有 5.86 ns/update,吞吐掉到 0.17 Tflop/s 量级,那测的是延迟不是吞吐。

跑起来是 288 block × 256 thread = 73,728 个线程同时在做,总共 8 × 73,728 ≈ 59 万条并发链,把 4,608 个 CUDA core 的发射口塞满。CSV 里的 operations 是这批的总 FLOP 数,value = operations / elapsed_ns,ns→s 的 1e9 和 G 的 1e9 抵消,比值直接是 Gflop/s。

延迟和吞吐是两回事,同一个 kernel 换个链宽就从一个变成另一个。 这也是为什么两组都要跑。

理论峰值怎么算

峰值 = SM 数 × 每 SM 的 CUDA core 数 × 每条 FMA 的 FLOP 数 × 时钟

     = 36    × 128                  × 2                  × 2.572 GHz
       ↑         ↑                      ↑                     ↑
   查询得到   公开资料假设          计数约定              查询得到

逐项来源:

项 值 来源
SM 数 36 cudaDevAttrMultiProcessorCount,查询得到
每 SM 的 CUDA core 数 128 假设。CUDA 不暴露这个数,是 Blackwell consumer 的公开资料值
每条 FMA 的 FLOP 数 2 行业约定
时钟 2572 MHz cudaDevAttrClockRate,标称 boost
    = 23,704 Gflop/s

为什么”每 core 每拍 1 条 FMA”就能当上限:NVIDIA 的 CUDA core 是标量 FMA 流水线,每条流水线每个时钟收 1 条 FFMA。4,608 条流水线 × 2 FLOP × 2.572 GHz = 23.7 Tflop/s,物理上再快不了 —— 要更高只能提频、加 core,或者换更宽的指令(FP16 一条顶两条)。

显存带宽的公式同理,而且更硬:

带宽 = 2 × 显存时钟 × 总线位宽 / 8
     = 2 × 14001 MHz  × 128 bit / 8
     = 448.0 GB/s
       ↑
   GDDR 每时钟双倍率,2 这个因子没有假设空间

为什么实测比”理论”还高

实测 FP32 是 25,423 Gflop/s,大于 23,704,看起来矛盾。原因是那个 2572 MHz 是标称值,不是实际跑起来的频率。A2 用 NVML 采了 30 秒持续负载:

  SM 时钟 功耗 温度
冷启动 817 MHz 6.5 W 37 °C
稳定后 2790–2797 MHz 101 W 56 °C

实际 boost 到了 2790 以上,超过标称的 2572。按 2790 重算:

36 × 128 × 2 × 2.790 GHz = 25,713 Gflop/s
25,423 / 25,713 = 98.9%

还有个反向的交叉验证:用同一个公式解时钟,

25,423 / (36 × 128 × 2) = 2.759 GHz = 2759 MHz

和遥测的 2790–2797 对得上(compute 组本身没采遥测,时钟是从 A2 推的,差的 1% 是测量本身的少量开销)。同一个公式既能算峰值也能反解时钟,两边自洽,才说明这 98.9% 不是巧合。

教训是:标称时钟不是测量条件,只是规格。 只要不锁频,就必须把实测时钟记下来,否则利用率这个比值没有意义。反过来,把时钟锁死(nvidia-smi -lgc)虽然让数字可复现,但测到的就不再是这张卡默认的持续表现。

利用率 98.9% 意味着什么、不意味着什么

意味着:FMA 发射口被打满了,这条指令在这个 microbench 里没有更深的优化空间。换句话说,测得的就是硬件的发射上限,不是”软件效率”。

不意味着:

同一条纪律适用于带宽:实测 412 GB/s 是理论 448 的 92%,这个 92% 才是有意义的数,412 本身不是。而且带宽的”理论”只扣了协议开销(GDDR 双倍率),没扣刷新、ECC(本卡无)、行冲突,所以永远到不了 100%。

精度之间为什么差这么多

FP8  / INT8 MMA  104  Tops     ← tensor core
FP16 / BF16 MMA   52  Tflop/s  ← tensor core,一条 MAC 顶两个 FP32 FLOP
FP32              25  Tflop/s  ← CUDA core
FP64             0.41 Tflop/s  ← 被砍到 1/62

FP8 是 FP32 的 4 倍、FP16 是 2 倍,不是”算得更快”,是数据宽度减半后同一块硅能塞下双倍的 MAC 单元,而且走的是 tensor core 而不是 CUDA core。FP64 是消费卡特意砍掉的(1/62 ≈ 1/64,GeForce 的典型策略,给专业卡留市场)。

两个额外的坑:

和 CPU 版的关系

cpu microbench 的 compute() 报 ns/update 不报 Gflop/s,因为单条依赖链上”吞吐”这个量没有意义。要拿 CPU 的峰值算力对照,同样得走一遍上面的公式:核数 × 每核每拍的 FMA 数 × FLOP 数 × 实测时钟 —— 而且 x86 的 FMA 是 AVX-512 之类的向量指令,”每条的 FLOP 数”是 16 或 32 而不是 2,计数约定一变,数字就差一个数量级。

跨架构比 FLOP 数几乎总是错的,能比的是同架构同计数下的利用率。 这也是为什么两套 microbench 的正文都坚持报 ns/update、GB/s 这类不依赖计数约定的量,把 Tflop/s 只当作派生指标。

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。