H200 · HOPPER GH100 · 特性专题
TMA · Tensor Memory Accelerator
★ Hopper 搬运主线 · 把地址计算与指令发射开销整个卸到硬件
是什么
DEFINITIONTMA 是 Hopper 引入的专用硬件拷贝单元——一条指令 + 一个 host 预编码的 tensor-map 描述符,由硬件完成整块多维 tile 在 global ↔ shared 之间的异步搬运(含 multicast 广播)。线程不再逐元素算地址、不再循环发小拷贝。
为什么需要(vs A100 cp.async)
RATIONALE · VS A100 CP.ASYNC| 维度 | A100 cp.async / LDGSTS | Hopper TMA |
|---|---|---|
| 发起粒度 | 逐线程,每线程算源/目地址 | 1 个线程(elect_one)发 1 条指令搬整块 tile |
| 寻址 | 线程内循环算 stride/offset | 硬件按 tensor-map 自动算多维地址 |
| 维度 | 仅 1D | 1D–5D(boxDim ≤ 256/维) |
| 完成同步 | commit + wait | mbarrier::complete_tx::bytes(记字节数) |
| 广播 | 无 | multicast::cluster,一份拷贝落多个 CTA smem |
| 越界处理 | 软件判断 | 硬件用 0 / NaN 填充 |
结果:地址计算开销归零;搬一个 128×128 tile 只占 1 线程的 1 条指令;multicast 省 L2/global 带宽。TMA + wgmma + mbarrier 三者构成 Hopper 的端到端异步流水,是 FlashAttention-3 / CUTLASS 双缓冲能真正重叠搬运与计算的前提。
关键数字
KEY NUMBERS · CONSTRAINTS| 项 | 值 / 约束 | 注 |
|---|---|---|
| tensor map 维度上限 | ≤ 5D(tensorRank ≤ 5) | cuTensorMapEncodeTiled |
| boxDim(每维 tile) | ≤ 256 | boxDim[0]×elemsize 必须 16B 对齐 |
| globalStrides | 16 的倍数,< 2⁴⁰ | 字节单位 |
| 地址对齐 | base 16B(INTERLEAVE_32B 要 32B) | CUtensorMap 结构自身 64B 对齐 |
| tensor map 创建位置 | HOST | 运行期换基址用 cuTensorMapReplaceAddress |
| 传 kernel 方式 | __grid_constant__ const CUtensorMap* | 强制 grid_constant 语义 |
| multicast 前提 | 必须 cluster launch | + 16-bit multicast_mask(每位=1个CTA) |
| 发射线程 | 整 CTA 仅 1 个(elect_one) | 多线程同发 UB |
可编译示例
COMPILABLE EXAMPLE · VERBATIM逐字摘自 learn-cuda/02e_matmul_sm100/common.h 与 CUTLASS copy_sm90_tma.hpp。
① Host 端:编码 2D tensor map
// 描述一个 bf16 [H,W] 矩阵,tile 尺寸 [sH,sW] 落到 shared mem
inline void init_tmap_2d(CUtensorMap* tmap, const nv_bfloat16* ptr,
uint64_t gH, uint64_t gW, uint32_t sH, uint32_t sW,
CUtensorMapSwizzle swizzle) {
constexpr uint32_t rank = 2;
uint64_t globalDim[rank] = {gW, gH}; // {宽,高}
uint64_t globalStrides[rank-1] = {gW * sizeof(nv_bfloat16)}; // 字节, %16==0
uint32_t boxDim[rank] = {sW, sH}; // tile in smem
uint32_t elementStrides[rank] = {1, 1};
cuTensorMapEncodeTiled(
tmap, CU_TENSOR_MAP_DATA_TYPE_BFLOAT16, rank, (void*)ptr,
globalDim, globalStrides, boxDim, elementStrides,
CU_TENSOR_MAP_INTERLEAVE_NONE, swizzle,
CU_TENSOR_MAP_L2_PROMOTION_NONE, CU_TENSOR_MAP_FLOAT_OOB_FILL_NONE);
}
② Device 端:发起 2D TMA global→shared + mbarrier 同步
// 单线程发起异步整块搬运;完成后 mbarrier 归零
__device__ __forceinline__ void tma_2d_g2s(
uint32_t smem_dst, const void* tmap, uint32_t x, uint32_t y, uint32_t mbar) {
asm volatile(
"cp.async.bulk.tensor.2d.shared::cta.global.mbarrier::complete_tx::bytes "
"[%0], [%1, {%2, %3}], [%4];"
:: "r"(smem_dst), "l"(tmap), "r"(x), "r"(y), "r"(mbar) : "memory");
}
// 使用顺序(cluster/单 CTA 通用):
// 1) mbarrier_init(mbar, 1)
// 2) mbarrier_arrive_expect_tx(mbar, tile_bytes) ← 预告期望字节数
// 3) if (elect_one) tma_2d_g2s(...)
// 4) mbarrier_wait_parity(mbar, phase) ← 等硬件搬完归零
③ Multicast 变体(cluster 内广播到多个 CTA)
// 逐字自 CUTLASS SM90_TMA_LOAD_MULTICAST_2D —— 必须在 cluster launch 下
asm volatile(
"cp.async.bulk.tensor.2d.shared::cluster.global.mbarrier::complete_tx::bytes"
".multicast::cluster.L2::cache_hint [%0], [%1, {%4,%5}], [%2], %3, %6;"
:
: "r"(smem_int_ptr), "l"(gmem_desc), "r"(smem_int_mbar),
"h"(multicast_mask), // 16-bit:每位 = cluster 内一个 CTA
"r"(crd0), "r"(crd1), "l"(cache_hint)
: "memory");
注意事项 / 陷阱
PITFALLS · CHECKLIST
CAUTION · 陷阱清单
- tensor map 必须 HOST 创建;运行期换基地址用
cuTensorMapReplaceAddress,不要在 device 侧重建。 - 传 kernel 必须
__grid_constant__ const CUtensorMap*——否则地址/语义 UB。 - base 16B 对齐(INTERLEAVE_32B 要 32B);swizzle 必须与 smem 访问布局一致,否则数据错位不报错。
- Multicast 必须 cluster launch,目标用
.shared::cluster;非 cluster 下用 multicast 是 UB。 - mbarrier 用前必须
mbarrier.init;先 arrive.expect_tx(size) 再发 TMA,顺序反了会少计字节。 - 整 CTA 只能 1 个线程(elect_one)发射
cp.async.bulk.tensor,多线程同发 UB。
数据源
SOURCES- CUDA Driver API — Tensor Map Object Management(cuTensorMapEncodeTiled 签名 + 全部约束)
- CUTLASS copy_sm90_tma.hpp(1D–5D G2S/S2G + MULTICAST inline asm)
- learn-cuda 02e/common.h(最小可编译 TMA + mbarrier 封装)
- PTX ISA — cp.async.bulk.tensor / mbarrier