峰值算力怎么算,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 里没有更深的优化空间。换句话说,测得的就是硬件的发射上限,不是”软件效率”。
不意味着:
- 不是”这张卡的 98.9% 性能”。 这是单一指令流、操作数全在寄存器、零访存的极端情形。真实 kernel 有访存、有分支、有同步,能贴到这个数的应用几乎不存在。
- 不是厂商标称数字的 98.9%。 厂商标的往往是不同精度的峰值,或者带 2 倍结构化稀疏的数。这次各精度是各测各的:FP32 25.4、FP16 MMA 52.2、FP8 104、FP64 0.408 Tflop/s —— 是四条不同的物理管线,不能混着比。
- 128 core/SM 这个假设本身没有独立证据。 它是公开资料值。98.9%
的利用率是对这个假设的一致性检验,不是证明:如果真实是
64,同一个数据算出来就是 198%,那说明公式错了;如果真实是 256,就会是
49%。正好落在 100% 附近,说明 128 这个数和整套计数是自洽的 —— 所以
gpu-microbench 的 A1 把
cuda_cores_per_sm_assumed单独出一行,而不是混进测量值里。
同一条纪律适用于带宽:实测 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 的典型策略,给专业卡留市场)。
两个额外的坑:
- FP16 峰值要问清楚累加精度和稀疏度。 这次的 52 Tflop/s 是 FP32 累加、稠密;厂商标的约 98 Tflop/s 通常是 FP16 累加或 2 倍结构化稀疏的数。同一条指令、同一个 tensor core,报出来的数能差一倍。
- wmma / mma 的形状影响 FLOP 计数。
m16n16k16每条 warp 级指令是 16×16×16×2 = 8192 FLOP,m16n8k32是 16×8×32×2 = 8192,但后者是 FP8/INT8 的形状。算 TOPS 时别把形状的 FLOP 数和数据类型的宽度搞混。
和 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 将按侵权追究法律责任,其它情况随意。