cp.async로 global→shmem 비동기가 도입되었으나, 발행 단위가 warp(32T)였고 Tensor Core 자체는 여전히 동기. Hopper는 TMA(1T issue) + WGMMA(async TC) + mbarrier(hw signal) 3종 셋으로 producer–consumer 분리를 ISA 수준에서 지원한다.
-arch=sm_90a 필수.
PTX ISA 8.0+ / CUDA 12.0+
Ampere Hopper
copy cp.async (warp) cp.async.bulk.tensor (1T)
MMA mma.sync (sync) wgmma.mma_async (async)
sync bar.sync mbarrier.{arrive|wait}
scope block cluster (block ×N)
shmem local only + remote (DSM)
공통: cp.async·mma.sync는 Hopper에서도 유효 ↗ V03 §6·§7
producer WG0 → [sA,sB] smem → consumer WG1
TMA issue mbarrier full WGMMA async
↑______ mbarrier empty ________|
| 이유 | 해결 수단 |
|---|---|
| TC throughput 증가 | WGMMA async → 동기 오버헤드 제거 |
| register 부족 | setmaxnreg 재분배 + shmem operand |
| L2 BW 재분배 | Cluster multicast + DSM |
출처: GH100 Whitepaper §3, PTX ISA 8.x §9.7
fence.proxy.async 필요st.shared로 overwrite하면 race. async proxy fence로 order 보장.
cluster.sync로 집합적 동기화// C++ attribute __global__ __cluster_dims__(2,1,1) void kernel(...){ ... } // PTX directive .reqnctapercluster 2, 1, 1 // launch API (dynamic) cudaLaunchKernelEx(&cfg, kernel, args);
cluster shape는 kernel-time 또는 launch-time 결정. cudaFuncSetAttribute(..., NonPortableClusterSize, 1)로 16 unlock.
| 변수 | 의미 |
|---|---|
clusterDim | cluster 내 block 수 (x,y,z) |
clusterIdx | grid 내 cluster 좌표 |
blockRank | cluster 내 block의 linear id ∈ [0, |cluster|) |
cluster.sync() | cluster 내 모든 thread 집합 동기 |
Cooperative Groups: cluster_group cg = this_cluster()
// cluster-wide barrier barrier.cluster.arrive; barrier.cluster.wait; // or fused barrier.cluster.sync; // rank query mov.u32 %r0, %cluster_ctarank; mov.u32 %r1, %cluster_nctaid.x;
cudaErrorLaunch
OutOfResources).
| 용도 | shape | 비고 |
|---|---|---|
| Hopper GEMM | (2,1,1) | multicast A tile |
| Hopper GEMM big | (2,2,1) | 4 block × N-way |
| Conv / Attn | (1,1,1) | cluster 미사용, WS만 |
| Max portable | (8,1,1) | spec 보장 최대 |
출처: CUTLASS 3.x SM90 collective.
.shared::cluster (확장된 smem window)| access | cycles (approx) |
|---|---|
| local smem | ~20 |
| DSM (cluster) | ~30–40 |
| L2 hit | ~200 |
| HBM | ~400–600 |
정확 수치는 V02 §14 ↗ V02. 여기는 비율만 기억.
// local smem ptr → remote smem ptr // target rank = r mapa.shared::cluster.u32 %r_remote, %r_local, %r_rank; // 이후 일반 ld/st.shared::cluster 사용 ld.shared::cluster.f32 %f0,[%r_remote]; st.shared::cluster.f32 [%r_remote],%f1;
mapa = map address. 단일 instruction, smem offset 보존.
auto cg = cg::this_cluster(); int myr = cg.block_rank(); int N = cg.num_blocks(); float* remote = cg.map_shared_rank(local_ptr, peer_rank); // remote는 유효한 .shared::cluster ptr cg.sync(); // cluster barrier
atom.shared::cluster.add 등| 패턴 | 용도 |
|---|---|
| A-multicast GEMM | 1 block이 A tile load → N block이 읽음 |
| Reduction across blocks | cluster 내 partial sum 병합 |
| Conv halo share | 인접 block의 edge row 참조 |
| Attention KV share | KV tile을 cluster 내 분산 저장 |
barrier.cluster.arrivebarrier.cluster.wait → load.shared::cluster로 초기화 후 arrive/wait.
__syncthreads()는 block-scope. DSM 대상은 절대 보장 못함.
| 필드 | 의미 |
|---|---|
| tensor ptr | global base address |
| rank | 1–5 |
| global dims | 각 축 크기 |
| global strides | 바이트 stride (축별) |
| box dims | tile 크기 (smem 복사 단위) |
| element stride | dilation (≥1) |
| dtype | FP16/BF16/FP8/TF32/… |
| swizzle | NONE / 32B / 64B / 128B |
| OOB fill | NONE / zero / NaN |
CUtensorMap desc; cuTensorMapEncodeTiled( &desc, CU_TENSOR_MAP_DATA_TYPE_FLOAT16, /*rank=*/2, g_ptr, /*dims=*/{M,N}, /*strides=*/{N*2, 2}, /*box=*/{BM, BN}, /*elemStride=*/{1,1}, CU_TENSOR_MAP_INTERLEAVE_NONE, CU_TENSOR_MAP_SWIZZLE_128B, CU_TENSOR_MAP_L2_PROMOTION_L2_128B, CU_TENSOR_MAP_FLOAT_OOB_FILL_NONE);
128-byte 정렬 · const로 kernel에 넘김 (__grid_constant__ CUtensorMap).
| mode | 의미 | 용도 |
|---|---|---|
| tile | box 단위 rectangular copy | GEMM, Transformer |
| im2col | conv sliding window → column | CNN, 2D conv |
im2col: descriptor의 interleave·lower/upper offset·kernel strides로 conv 변환.
[thread 0] [TMA engine] [mbarrier]
init desc ─ init(1)
expect_tx(bytes)────────────────────────▶ set TX count
cp.async.bulk.tensor ──▶ fetch tile
│
▼
write smem
arrive.complete_tx──▶ TX count↓
try_wait.parity ◀──────────────────────── phase flip
consume smem
// thread 0만 발행 if (threadIdx.x == 0) { asm volatile( "cp.async.bulk.tensor.2d" ".shared::cluster.global" ".mbarrier::complete_tx::bytes" " [%0], [%1, {%2,%3}], [%4];" :: "r"(smem_ptr), // dst "l"(tma_desc), // CUtensorMap* "r"(coord_x), "r"(coord_y), "r"(bar_ptr)); // mbarrier }
bulk_group (commit_group/wait_group 기반)// === setup (thread 0) === if (tid == 0) { mbarrier_init(bar, 1); fence_mbarrier_init(); } __syncthreads(); // === phase 0: issue === if (tid == 0) { mbarrier_expect_tx(bar, BM*BN*2); cp_async_bulk_tensor_2d( sA, &tma_desc, {cx, cy}, bar); } // === wait (all threads) === uint32_t phase = 0; while (!mbarrier_try_wait_parity( bar, phase)) { } // === consume === wgmma_fence(); wgmma(acc, sA, sB); wgmma_commit_group(); wgmma_wait_group(0); // === next iter: toggle phase === phase ^= 1;
| 명령 | 용도 |
|---|---|
cp.async.bulk | G→S tile load |
cp.async.bulk | S→G store |
cp.async.bulk | cluster multicast |
cp.async.bulk | L2 prefetch |
cp.async.bulk | commit 그룹 닫기 |
cp.async.bulk | N개 미만 남을 때까지 대기 |
// N-bit mask: cluster rank별 수신 여부 asm("cp.async.bulk.tensor.2d" ".shared::cluster.global" ".multicast::cluster" ".mbarrier::complete_tx::bytes" " [%0],[%1,{%2,%3}],[%4],%5;" :: ... , "h"(mask));
mask = 0b0011 → rank 0·1 수신.
st.shared로 바로 overwrite → async proxy race.
② mbarrier init은 1 thread + fence.
③ descriptor는 128B aligned, const 선호.
[63] phase bit [62:44] expected arrive count [43:25] current arrive count [24: 0] pending TX bytes (TMA용)
정확한 bit layout은 구현 dependent. 의미만 사용.
init — expected count 설정 + phase=0expect_tx — TMA가 들어올 byte 수 예약 (optional)arrive — count++ (thread 단위)arrive.expect_tx — count++ & TX 예약try_wait.parity — phase 반전 때까지 spin| 명령 | 주체 | 효과 |
|---|---|---|
mbarrier | 1 thread | count 설정 |
mbarrier | 1 thread | 소멸 |
mbarrier | 각 thread | 도착 signal |
mbarrier.arrive | 1 thread | 도착+TX 예약 |
mbarrier | 1 thread | TX 예약만 |
mbarrier | 각 thread | phase 체크 |
mbarrier | 각 thread | non-blocking 체크 |
// init (1 thread) asm("mbarrier.init.shared.b64" " [%0], %1;" :: "r"(bar), "r"(N_ARRIVE)); fence_mbarrier_init_release(); // arrive (각 thread) asm("mbarrier.arrive.shared.b64" " _, [%0];" :: "r"(bar)); // wait uint32_t p = 0; while(!try_wait_parity(bar, p)){} p ^= 1;
iter 0: init→arrives→flip(0→1) wait p=0 iter 1: arrives→flip(1→0) wait p=1 iter 2: arrives→flip(0→1) wait p=0 ... phase ^= 1 매 iter
| signal 주체 | waiter | 예 |
|---|---|---|
| TMA (HW) | consumer warp | complete_tx |
| producer warp | consumer WG | full barrier |
| consumer WG | producer warp | empty barrier |
| cluster peers | this block | cluster barrier |
fence.mbarrier_init.release.cluster 없이 다른 warp가 arrive 시도하면 UB.
wgmma.mma_async 호출 — lane divergence 금지wgmma.fence ; 이전 reg writes flush wgmma.mma_async ; 발행 후 즉시 다음 명령 가능 wgmma.commit_group ; 지금까지 발행된 mma 그룹 닫기 wgmma.wait_group N ; N개 group 이하만 in-flight 유지
commit/wait 쌍으로 pipeline 제어. N=0이면 모든 in-flight 완료 대기.
| 피연산자 | 위치 | 형태 |
|---|---|---|
| A | RF or smem | register tile / matrix desc |
| B | smem only | matrix desc (64-bit) |
| D (accum) | RF | per-thread fragment |
// === setup === uint64_t descA = make_desc(sA, stride, swizzle); uint64_t descB = make_desc(sB, stride, swizzle); float d[64]; // accumulator fragment // === mainloop === asm("wgmma.fence.sync.aligned;"); asm volatile( "wgmma.mma_async.sync.aligned" ".m64n256k16" ".f32.f16.f16" " {%0,%1,...,%127}," // D: 128 regs " %128, %129," // descA, descB " %130, 1, 1, 1, 1;" // scaleD,A,B,tnsA,tnsB : "+f"(d[0]), "+f"(d[1]), ... : "l"(descA), "l"(descB), "r"(scaleD)); asm("wgmma.commit_group.sync.aligned;"); asm("wgmma.wait_group.sync.aligned 0;"); // d[] 이제 consumption 가능
[63:62] base offset (smem alignment) [61:49] leading dim BO (byte offset between 8·k tiles) [48:46] swizzle (0=none,1=32B,2=64B,3=128B) [45:32] stride dim BO (byte offset between groups of 8 rows) [31:14] reserved [13: 0] start address (smem offset / 16)
CuTe의 make_gmma_desc가 자동 생성. 수동 생성은 silent 오답 유발.
| imm | 의미 |
|---|---|
| scaleD | 1 = D += A·B, 0 = D = A·B |
| scaleA/B | ±1 (부호) |
| tnsA/B | 0=NN, 1=transposed |
mma.sync(Ampere): warp(32T), sync, reg만.
wgmma.mma_async(Hopper): WG(128T), async, B는 smem.
↗ V03 §7
D[m64, nN] = A[m64, kK] · B[kK, nN]
↑ ↑ ↑
accumulator SM-input-A SM-input-B (smem)
출처: PTX ISA 8.x §9.7.14.
| N_C tile | 권장 WGMMA N |
|---|---|
| 64 | 64 |
| 128 | 128 |
| 192 | 192 (=8·24) |
| 256 | 256 (peak) |
N 클수록 발행 횟수 ↓ → issue overhead ↓.
| 입력 A/B | accum D | K | 명령 접미 |
|---|---|---|---|
| FP16 / FP16 | FP32 | 16 | .f32.f16.f16 |
| FP16 / FP16 | FP16 | 16 | .f16.f16.f16 |
| BF16 / BF16 | FP32 | 16 | .f32.bf16.bf16 |
| TF32 / TF32 | FP32 | 8 | .f32.tf32.tf32 |
| E4M3 / E4M3 | FP32 | 32 | .f32.e4m3.e4m3 |
| E4M3 / E5M2 | FP32 | 32 | .f32.e4m3.e5m2 |
| E5M2 / E4M3 | FP32 | 32 | .f32.e5m2.e4m3 |
| E5M2 / E5M2 | FP32 | 32 | .f32.e5m2.e5m2 |
| S8 / S8 | S32 | 32 | .s32.s8.s8 |
| U8 / U8 | S32 | 32 | .s32.u8.u8 |
| S8 / U8 | S32 | 32 | .s32.s8.u8 |
| B1 / B1 (XOR.POP) | S32 | 256 | .s32.b1.b1 |
FP8: E4M3=더 정밀·낮은 range, E5M2=높은 range·낮은 정밀 ↗ V09 §5.
| shape | accum dtype | regs/T |
|---|---|---|
| m64n64k16 | FP32 | 32 |
| m64n128k16 | FP32 | 64 |
| m64n256k16 | FP32 | 128 |
| m64n128k16 | FP16 | 32 |
128T × 128 reg = 16K reg/WG → consumer에 몰아주기 필요.
setmaxnreg 240+ 필요.
↗ §10 p.11
wgmma.mma_async.sp.sync.aligned| config | producer | consumer |
|---|---|---|
| 1P+1C | WG0 (warp 0–3) | WG1 (warp 4–7) |
| 1P+2C (pingpong) | WG0 | WG1, WG2 |
| 1P+1C+1E | WG0 | WG1 |
E=epilogue. FA3 커널은 1P+2C. CUTLASS GEMM은 config 선택.
// === shared mbarriers === __shared__ uint64_t bar_full[S]; __shared__ uint64_t bar_empty[S]; // === init (1 thread) === if (tid == 0) { for (i=0; i<S; i++){ mbarrier_init(&bar_full[i], 1); mbarrier_init(&bar_empty[i], 2); // 2 cons WG } fence_mbarrier_init_cluster(); } __syncthreads(); // === PRODUCER warp-group (WG0) === if (wg_idx == 0) { setmaxnreg_dec(40); // 240 → 40 uint32_t p = 0; for (k=0; k<K_TILES; k++){ int slot = k % S; mbarrier_wait_parity( &bar_empty[slot], p); if (lane==0) { mbarrier_expect_tx( &bar_full[slot], BM*BK*2 + BK*BN*2); cp_async_bulk_tensor( &sA[slot], &desc_A, {k*BK, m0}, &bar_full[slot]); cp_async_bulk_tensor( &sB[slot], &desc_B, {n0, k*BK}, &bar_full[slot]); } if ((k%S) == S-1) p ^= 1; } }
// === CONSUMER warp-groups (WG1, WG2) === else { setmaxnreg_inc(232); // 40 → 232 float acc[128] = {0}; uint32_t p = 0; for (k=0; k<K_TILES; k++){ int slot = k % S; mbarrier_wait_parity( &bar_full[slot], p); wgmma_fence(); wgmma_mma_async( acc, make_desc(&sA[slot]), make_desc(&sB[slot])); wgmma_commit_group(); wgmma_wait_group(1); // 1 in-flight mbarrier_arrive( &bar_empty[slot]); if ((k%S) == S-1) p ^= 1; } wgmma_wait_group(0); store_epilogue(acc); }
| 패턴 | consumer 분배 |
|---|---|
| Cooperative | 같은 M tile의 N축 절반씩 |
| Pingpong | 다른 M tile 교대 |
상세 trade-off ↗ V06 §15, ↗ V07 §10.
thread ──generic──▶ smem/L2/HBM TMA ──async ────▶ smem/L2/HBM WGMMA ──async ────▶ RF/smem(read)
| 명령 | 효과 |
|---|---|
fence.proxy | async→generic(smem) |
fence.proxy | async→generic(gmem) |
fence.mbarrier_ | init 가시성 |
wgmma.fence | WGMMA 전 RF commit |
ld.shared): 명시 fence 필요wgmma.wait_group이 release 역할setmaxnreg.dec.sync.aligned.u32 %R; // reduce to R setmaxnreg.inc.sync.aligned.u32 %R; // raise to R
| 항목 | 값 |
|---|---|
| 허용 R | 24, 32, 40, ... , 240 (8의 배수) |
| dec 대상 기본 | initial max nregs |
| inc 상한 | 240 (일반) / 224 (setup 제한 시) |
| 동기 | WG 전원이 같은 call |
PTX ISA 8.x. __nv_cvta_set_maxnreg_dec_sync_aligned_u32 builtin.
FA3 (Hopper) 기본값: prod 40, cons 232 (H100 SM).
if (wg_idx == 0) { setmaxnreg_dec(40); producer_loop(); } else { setmaxnreg_inc(232); consumer_loop(); }
__launch_bounds__(N, M)는 initial max reg/T 결정. setmaxnreg는 그 범위에서만 유효. initial이 너무 작으면 inc(232) 실패.
cuobjdump --dump-resource-usage로 per-kernel reg 확인출처: GH100 Whitepaper §3.3, CUDA Math API.
| PTX | 동작 |
|---|---|
vimin3.s32 | min(a,b,c) |
vimax3.s32 | max(a,b,c) |
vimin3.s16x2 | 2×min, 16-bit packed |
vimax3.s16x2 | 2×max, packed |
vibmax3.s32 | max + index |
// y = max(x+bias, 0) + c, 단일 inst asm("viaddmax.s32.relu" " %0, %1, %2, %3;" : "=r"(y) : "r"(x), "r"(bias), "r"(c)); // 3-arg max with saturate asm("vimax3.s32" " %0, %1, %2, %3;" : "=r"(m) : "r"(a), "r"(b), "r"(c));
vimax3 + viaddmax로 2 inst로 compact.
| C builtin | PTX |
|---|---|
__vimax3_s32 | vimax3.s32 |
__vimin3_s32 | vimin3.s32 |
__vibmax_s32 | vibmax3 (idx) |
__viaddmax_ | viaddmax.relu |
__viaddmin_ | packed |
unsigned, signed, packed 버전 합쳐 약 20개.
fma로 처리.
ptxas → SASSnvdisasm, cuobjdump.cu ─[nvcc front]─▶ .ptx .ptx ─[ptxas]──────▶ .cubin (SASS + metadata) .cubin ─[driver JIT]─▶ (native) → launch
embedded PTX는 driver가 최종 SM에 대해 runtime compile (fat binary).
| arch | word | 특징 |
|---|---|---|
| Fermi | 64-bit | 구조적 단순 |
| Maxwell/Pascal | 64-bit + 3-inst ctrl | control word 분리 |
| Volta/Turing | 128-bit | inst당 ctrl 내장 |
| Ampere/Hopper | 128-bit | + async ops, mbar, WGMMA |
출처: Jia et al. 2018 / Dissecting Volta · Ampere reverse-engineering 논문.
LDL/STL 존재)# cubin에서 SASS 덤프 nvdisasm --print-code kernel.cubin # control code 함께 nvdisasm --print-instruction-encoding \ --print-line-info kernel.cubin # cuobjdump from fatbin cuobjdump --dump-sass a.out
mad.rn.f32 → SASS FFMA. PTX mma.sync → SASS HMMA.
[label] OPCODE.MOD [P?] dst, src1, src2 [, src3] ; 예: /*0010*/ FFMA R3, R2, R5, R3 ; /*0020*/ @P0 BRA 0x50 ; /*0030*/ STG.E [R6], R4 ;
@P0: predicate (0~6).E: effective addr (64-bit pointer)R0..R255 + RZ(=0), URx(uniform)| SASS (Hopper) | PTX 대응 |
|---|---|
UBLKCP | cp.async.bulk |
UTMALDG/UTMASTG | cp.async.bulk.tensor |
MBARRIER_* | mbarrier.* |
GMMA | wgmma.mma_async |
SETMAXREG | setmaxnreg.{inc,dec} |
opcode 이름은 nvdisasm 버전에 따라 약간 다를 수 있음.
| opcode | 의미 |
|---|---|
IMAD | int multiply-add (포인터 계산 주력) |
IADD3 | 3-op integer add (주소+offset+base) |
IMUL | int mul |
FFMA | float fused multiply-add (FP32 core 주력) |
FMUL/FADD | float mul/add |
HFMA2 | FP16×2 packed FMA |
DFMA | double fused MA |
| opcode | 의미 |
|---|---|
LDG.E | load global (effective addr) |
STG.E | store global |
LDS | load smem |
STS | store smem |
LDL/STL | local mem (spill 신호) |
LDGSTS | Ampere cp.async |
LDSM | ldmatrix |
| opcode | 의미 |
|---|---|
HMMA | Half-precision MMA (FP16/BF16) |
IMMA | Integer MMA (INT8) |
BMMA | Binary MMA (1-bit) |
QMMA | FP8 quarter-precision (Hopper) |
GMMA | group MMA = WGMMA (Hopper) |
HMMA는 warp-level, GMMA는 warpgroup-level async.
| opcode | 의미 |
|---|---|
BRA | branch |
BRX | indexed branch |
JMP | jump (long) |
BSSY/BSYNC | convergence barrier (Volta+) |
EXIT | thread 종료 |
CALL/RET | function |
| opcode | 의미 |
|---|---|
BAR.SYNC | __syncthreads |
MEMBAR | fence |
ATOMS | atomic smem |
ATOMG | atomic global |
RED.E | reduction global |
| mod | 의미 |
|---|---|
.E | effective 64-bit addr |
.128 | 128-bit vectorized |
.CA/.CG | cache L1+L2 / L2 only |
.CS | streaming (non-coherent) |
.SYS | system-scope atomic |
.STRONG | strong ordering |
/*0100*/ IMAD.WIDE R4, R3, 0x4, R0 ; // addr calc /*0108*/ ULDC.64 UR4, c[0x0][0x160] ; // desc /*0110*/ LDS.128 R8, [R4+0x0] ; // smem load /*0118*/ GMMA.64x256x16.F32.F16 R16, desc_A, desc_B, R16 ; /*0120*/ GMMA.FENCE ; /*0128*/ GMMA.COMMIT_GROUP ; /*0130*/ GMMA.WAIT_GROUP 0x0 ; /*0138*/ STG.E.128 [R20], R16 ;
주소·opcode는 예시용. 실제 nvdisasm 출력과 다를 수 있음.
-v로 확인. 해결: __launch_bounds__ 조정·loop 분할.
128-bit SASS inst = [64b opcode] [64b operand/ctrl]
control field ≈ 16-bit subset
| field | bit | 역할 |
|---|---|---|
| stall | 4 | 다음 inst 전 대기 cycle (0~15) |
| yield | 1 | 다른 warp 양보 hint |
| write barrier | 3 | SB# 설정 (이 inst 결과 ready 신호) |
| read barrier | 3 | SB# 대기 |
| wait mask | 6 | 대기할 SB 비트맵 |
| reuse | 4 | operand cache reuse flag |
SB = ScoreBoard. 6개 slot. fixed-latency는 stall만, variable(LDG/HMMA)는 SB 사용.
/*0200*/ LDG.E.SB0 R4, [R2] ; // write SB0 /*0208*/ IADD3 R5, R5, R0, RZ ; // stall=1 /*0210*/ @SB0 FFMA R6, R4, R7, R6 ; // wait SB0 /*0218*/ FFMA.REUSE R8, R4, R9, R8; // reuse R4
@SB0 표기는 툴에 따라 WAIT(0) 등으로 나타남.
GMMA.WAIT_GROUP이 GMMA SB drain| 카테고리 | 처리 |
|---|---|
| fixed 고정 (FFMA, IADD) | stall 필드 |
| variable 메모리 (LDG, LDS) | write/wait SB |
| async (cp.async, TMA) | mbarrier/그룹 |
| TC (HMMA, GMMA) | dedicated SB (Hopper) |
Nsight Compute의 stall reason 분류는 이 control code를 집계한 것. ↗ V18 §4.
| A / B | dual 가능? |
|---|---|
| FFMA + LDG | ○ (다른 unit) |
| FFMA + IMAD | △ (unit 중복) |
| FFMA + FFMA | × |
| LDS + FFMA | ○ |
| HMMA + anything | × (TC 전용) |
실제 규칙은 compiler 전담. 사용자는 관찰만.
warp scoreboard: [SB0 SB1 SB2 SB3 SB4 SB5]
▲ ▲
| |
LDG.E → set SB0 HMMA → set SB4
@SB0 FFMA waits for SB0 == 0
| SB group | 추적 대상 |
|---|---|
| 일반 SB | LDG/LDS/STG 등 |
| GMMA SB | WGMMA (group 단위) |
| mbarrier | TMA (SB 미사용) |
-Xptxas -O3: aggressive reorder#pragma unroll / loop hoist가 체인 길이 결정| hazard | 해결 |
|---|---|
| RAW fixed | stall cycles |
| RAW variable | scoreboard wait |
| WAW | rename (HW) |
| WAR | operand latch |
| struct (unit busy) | issue slot block |
| 툴 | 용도 |
|---|---|
cuobjdump | fatbin/ELF → SASS 덤프 |
nvdisasm | cubin → SASS (text) |
nvcc -ptx | ptx 단독 출력 |
ptxas -v | reg/smem/lmem 사용량 |
Nsight Compute | source↔SASS 매핑 UI |
# 1. arch 지정 컴파일 + lineinfo nvcc -arch=sm_90a -lineinfo \ -Xptxas -v -o k.cubin k.cu # 2. PTX nvcc -arch=sm_90a -ptx k.cu -o k.ptx # 3. SASS + line nvdisasm --print-line-info \ --print-instruction-encoding \ k.cubin > k.sass # 4. PTX ↔ SASS side-by-side diff -y k.ptx k.sass | less
| PTX | SASS (Hopper) |
|---|---|
mad.rn.f32 | FFMA |
ld.global.f32 | LDG.E |
ld.shared.f32 | LDS |
st.global.v4 | STG.E.128 |
cp.async.ca.b128 | LDGSTS |
cp.async.bulk | UTMALDG |
mbarrier.arrive | MBARRIER.ARRIVE |
wgmma.mma_async | GMMA |
setmaxnreg.dec | SETMAXREG |
bar.sync | BAR.SYNC |
| 증상 | SASS 단서 |
|---|---|
| reg spill | LDL/STL 등장 |
| bank conflict | LDS 다수 + 긴 stall |
| 동기 GEMM | HMMA (GMMA 아님) |
| TMA 미사용 | UBLKCP 없음, LDGSTS만 |
| uncoalesced G load | LDG.E 반복, 32B 이하 |
| divergent branch | BSSY/BSYNC 빈번 |
.L_LOOP_START:
@SB0 LDS ... ;
GMMA ... ;
GMMA.COMMIT_GROUP ;
GMMA.WAIT_GROUP 1 ;
MBARRIER.ARRIVE ... ;
BRA .L_LOOP_START ;
mainloop는 보통 BSSY/BRA 쌍 + stage pipeline. MBARRIER가 경계.
-lineinfo 필수 (-G는 debug, 성능 왜곡)| 기능 | 핵심 명령 |
|---|---|
| TMA load | cp.async.bulk.tensor.{N}d.shared::cluster.global.mbarrier::complete_tx::bytes |
| WGMMA | wgmma.mma_async.sync.aligned.m64n{N}k{K}.dD.dA.dB |
| mbarrier | mbarrier.{init,arrive,expect_tx,try_wait.parity}.shared.b64 |
| Cluster | barrier.cluster.sync, mapa.shared::cluster |
| Reg redist | setmaxnreg.{inc,dec}.sync.aligned.u32 |
init(bar, 1); fence_init();
expect_tx(bar, BYTES);
cp.async.bulk.tensor(dst, desc, {x,y}, bar);
while(!try_wait_parity(bar, p)){}
p ^= 1;
wgmma.fence;
wgmma.mma_async.m64n256k16.f32.f16.f16
{D[0..127]}, descA, descB,
scaleD, 1, 1, 1, 1;
wgmma.commit_group;
wgmma.wait_group 0;
if (wg == 0) { // producer setmaxnreg_dec(40); for(k : K_TILES) { wait_empty(k); tma_load_A(k); tma_load_B(k); // → bar_full arrive } } else { // consumer setmaxnreg_inc(232); for(k : K_TILES) { wait_full(k); wgmma(acc, sA[k], sB[k]); wgmma_wait(1); arrive_empty(k); } epilogue(acc); }
| role | reg/T |
|---|---|
| producer WG | 40 |
| consumer WG | 232 |
| cooperative 양쪽 | 160·160 |
| opcode | 의미 |
|---|---|
IMAD | int MA (addr) |
FFMA | float FMA |
HFMA2 | FP16×2 FMA |
LDG.E / STG.E | global ld/st |
LDS / STS | smem ld/st |
LDSM | ldmatrix |
HMMA / GMMA | TC MMA (sync/async) |
UTMALDG | TMA load |
MBARRIER.* | barrier ops |
BAR.SYNC | __syncthreads |
-arch=sm_90 ≠ sm_90a (TMA/WGMMA 미생성)관련 권: V02 HW 정량, V03 Ampere PTX, V06 CUTLASS, V07 FA3. ↗ V02·V03·V06·V07