
这是一篇为 CUDA 开发者准备的博客旨在总结 CUDA 编程中至关重要的硬件参数和延迟数据。0. 序在高性能计算HPC和深度学习领域写出“能跑”的 CUDA 代码并不难但要写出“极致性能”的代码则需要对底层硬件有深刻的理解。就像 Jeff Dean 曾经列出的“每个程序员都应该知道的延迟数字”一样GPU 编程也有属于它的黄金数字。忽略它们你的 GPU 可能只发挥了 5% 的功力掌握它们你才能真正榨干显卡的每一滴算力。本文将带你梳理那些影响 CUDA 性能的关键常数与量级。1. 核心执行单元Warp Size 32这是 CUDA 编程中最著名的数字。Warp线程束是 GPU 执行指令的最小基本单位。含义 SM流多处理器一次调度 32 个线程执行相同的指令SIMT - 单指令多线程。性能启示分支发散 (Branch Divergence)如果一个 Warp 内的 32 个线程在同一时刻执行不同控制流路径硬件仍以 SIMT 方式执行各路径被谓词/掩码串行走完活跃线程数随路径切换而变吞吐按“有效并发”打折。Volta 及之后的独立线程调度主要改善within-warp的同步与收敛语义并降低部分场景下因__syncwarp()等带来的额外约束但 warp 内仍是一条指令广播给 32 线程——分歧依然有成本别指望“独立调度分歧免费”。内存合并 (Coalescing) 只有当一个 Warp 内的线程访问连续对齐的内存地址时才能实现最佳的内存吞吐量。Active Masks 在使用 Warp 级原语如 __shfl_sync时你需要意识到掩码通常是 32 位的。尾部效应 如果你的总线程数不能被 32 整除最后的一个 Warp 会有部分线程处于非活跃状态但仍会占用硬件资源。潜台词若某次 grid/block 里活跃线程总数很少不仅难以占满 SM还会出现未填满的尾部 Warp部分 lane 不参与实际计算却仍随 Warp 调度更容易暴露访存与指令延迟。更实用的说法是尽量保证有足够多的 Warp 在跑并让问题规模与线程组织方式能把 SIMD 宽度“吃满”。并不是说业务上永远不能 launch 非 32 倍数的线程数。2. 内存层级与延迟 (Latencies)理解内存延迟是优化的核心。GPU 是吞吐量导向的设备旨在通过大量的线程切换来掩盖延迟但延迟本身依然存在物理限制。以下给出 Ampere/Hopper 量级上的数量级不同 SKU、时钟、是否命中缓存、访存模式都会显著改变实测周期勿当精确规格表背诵。存储类型GPU 时钟周期 (Cycles)物理位置备注Registers 寄存器0 ~ 1SM 内部最快但数量有限还需警惕指令间的读后写RAW延迟常需 20 周期Shared Memory/L1 Cache~20 ~ 30SM 内部用户可控的高速缓存需注意 Bank ConflictL2 Cache~200GPU 全局共享芯片上最后一道防线Global Memory~400 ~ 800HBM/GDDR主要瓶颈所在关键数字32 Bytes在当代 NVIDIA GPU 上L2 常见以 32-byte sector 为寻址/传输的较细粒度各级缓存的 line 长度与标签粒度因架构与层级而异不必死记“一律 128-byte line 4×sector”但离散访存往往以 sector 为单位拉取因此合并访问、对齐与局部性仍然极其重要。128 Bytes一个 Warp 对连续float做向量化/合并加载时常对应 32×4B 128B 的合并访存模式硬件侧可能表现为若干 32B sector 的高效组合而不是“总是一笔 128B 原子事务”。100为了尽量掩盖全局内存 cache miss 的数百周期延迟往往需要大量可并发隐藏的 warp具体要多少取决于访存强度、指令级并行、occupancy 与架构细节“每 SM 上百 warp”这类口号不如用 profiler如 Nsight Compute看 stall 原因来得靠谱。3. Shared Memory Banks32 路与 4 字节共享内存Shared Memory在常见 32-bit 访存如float下可理解为被划分为 32 个 bank每个 bank 通常以 4 字节宽交错映射地址具体 bank 公式以《CUDA C Programming Guide》对你目标 compute capability 的说明为准双宽/某些特殊模式会改变冲突画像。Bank Conflict存储体冲突同一 warp 内若有多路访问落在同一 bank 的不同地址上硬件需把访问拆成多次串行服务体现为额外延迟与有效带宽下降。最坏情况32-way conflict 表示理想情况下一次 warp 访存被劈成约 32 段才能服务完周期数会大于 32还需结合访存指令自身延迟与架构微结构下面的除法仍是有效的定性心智模型。Effective Bandwidth Peak Bandwidth / Conflict Degree4. 数据传输PCIe vs. NVLink永远记住数据搬运是性能杀手。任何 Host (CPU) 与 Device (GPU) 之间的数据传输都极其昂贵。PCIe Gen4 x16: 单向理论有效带宽约 31~32 GB/s16 GT/s ×16 lane再扣 128b/130b 编码市场上常把双向合计口语化成约 64 GB/s请勿与“单向 64 GB/s”混淆。实测 goodput 常低于理论峰值除协议开销外还与是否 pinned (cudaHostRegister/cudaMallocHost)、是否异步流水线、拷贝粒度与 TLP、Root Complex/CPU 拓扑等有关单向 25~30 GB/s 在不少桌面/工作站平台上比较常见偏低时要先排查链路是否被折成 x8、是否走 PCH、以及拷贝路径。PCIe Gen5 x16: 单向理论有效带宽约 63~64 GB/s双向合计常称约 128 GB/s同上别与单向混淆。NVLink以 H100 系为例公开资料里常见 ~900 GB/s 量级多指机型/拓扑下多链路聚合的 GPU-GPU 互联带宽峰值具体到单条链路/单机内可路由路径请以对应白皮书与系统规格为准。GPU 显存带宽H100 HBM3 量级峰值约 3.35 TB/s3350 GB/s一档仍与 SKU、散热与实测 sustained 有别。性能启示把 PCIe 有效吞吐和 HBM 峰值带宽直接相除得到的是数量级常见约百分之几优化时更应关心是否频繁 host/device 往返、是否可融合/驻留显存、是否可 overlap 计算与拷贝。结论尽可能将计算留在 GPU 上即便某些步骤 GPU 并不绝对擅长也常被 PCIe 往返更划算仍要结合精度、库与工程约束综合判断。5. Kernel Launch Overhead常见「几微秒」量级启动一个 Kernel 并不是免费的主机侧把 work 提交给 GPU 往往要 ~几 µs常见经验值 ~3–10 µs 都存在冷启动、驱动/电源状态、同步方式、以及是否在同一 stream 已连续提交都会让你测到的数字差很多CUDA Graph 重放可把每次提交摊薄到更低。若某个 kernel 本体只有 ~1 µs 量级甚至更短经常出现启动与调度开销压过计算的现象。对策CUDA Graph、合并小 kernelfusion、减少不必要的同步、以及用 batch 让小任务“长得像一个大任务”。总结优化清单在编写下一行 CUDA 代码前请问自己我的 grid/block 是否提供了足够的 warp/occupancy 来隐藏访存与指令延迟别只盯着“32”要看活跃 warp 数与寄存器/shared 等资源约束。我的全局访存是否合并/共线warp 常见合并模式常与 32×4B128B 及其 32B sector 组合相关以 Nsight Compute 的 memory 指标为准。我是否避免了 Bank Conflict(32 banks)我是否在用 PCIe 传输小数据(Bandwidth limitations)我的 Kernel 足够大吗(Launch overhead)本文首发于 https://github.com/WingEdge777/vitamin-cuda, 可以随意转载同步发布于个人博客https://www.baizeway.com/article/548cec5dfba296d5欢迎关注