.cu --(nvcc front)--> .ptx .ptx --(ptxas)--> .cubin (SASS) .cubin ──pack──> fatbinary fatbin + embedded PTX ──> application // runtime (JIT) path: driver.loadModule(ptx) → ptxas(JIT) → SASS
Source: CUDA C PG Compilation 섹션 (13.1)
| 문자열 | 의미 | 대상 |
|---|---|---|
compute_80 | PTX virtual arch | Ampere GA100 |
sm_80 | SASS real arch | A100 |
sm_86 | SASS real arch | GA10x 소비자 |
sm_90 | SASS real arch | Hopper H100 |
sm_90a | arch-specific | TMA/WGMMA 용 |
~/.nv/ComputeCache| PTX ver | 도입 Arch | 대표 기능 |
|---|---|---|
| 7.0 | sm_80 | cp.async, mma BF16/TF32 |
| 7.1 | sm_86 | ldmatrix 확장 |
| 7.5 | sm_86 | 일반 개선 |
| 7.8 | sm_90 | TMA, WGMMA, cluster |
| 8.0 | sm_90 | tensormap |
| 8.3+ | sm_90a | arch-specific 확장 |
cf. PTX ISA 8.4 (CUDA 12.4) 기준
asm로 intrinsic 없는 명령 호출fma가 났는가)mad.lo.s32 → SASS IMADld.global → SASS LDG.E.128mma.sync → SASS HMMA.16816.F32# .ptx 파일 직접 보기 nvcc -ptx kernel.cu -arch=sm_80 # PTX + SASS 모두 nvcc -keep kernel.cu -arch=sm_80 # → kernel.ptx, kernel.cubin 생성 # cubin에서 SASS 역) cuobjdump --dump-sass kernel.cubin
// PTX skeleton .version 7.8 .target sm_80 .address_size 64 .visible .entry mykernel( .param .u64 mykernel_param_0, .param .u32 mykernel_param_1 ) { .reg .b32 %r<8>; .reg .b64 %rd<4>; .reg .pred %p<4>; // body ret; }
| directive | 역할 |
|---|---|
.version | PTX ISA 버전 |
.target | 대상 sm_XX |
.address_size | 32 / 64 |
.entry | kernel 함수 |
.func | device 함수 |
.visible | external 노출 |
.param | 인자 선언 |
.reg | register 선언 |
.shared | shared mem 변수 |
// 예
@%p1 add.s32 %r3, %r1, %r2;
mad.lo.s32 %r5, %r3, %r4, %r5;
ld.global.nc.f32 %f1, [%rd1];
st.shared.v4.f32 [%rs0], {%f0,%f1,%f2,%f3};
| type | 크기 | 해석 |
|---|---|---|
.u8 .u16 .u32 .u64 | 1~8B | unsigned int |
.s8 .s16 .s32 .s64 | 1~8B | signed int |
.b8 .b16 .b32 .b64 .b128 | 1~16B | opaque bit |
.f16 .f32 .f64 | 2~8B | IEEE float |
.bf16 | 2B | brain float |
.tf32 | 19-bit | TC-only, 32b 폭 |
.f16x2 .bf16x2 | 4B | packed 2-way |
.pred | 1-bit | predicate |
| prefix | type | 관례 |
|---|---|---|
%r | .b32 / .u32 / .s32 | general 32-bit |
%rd | .b64 / .u64 | address 64-bit |
%f | .f32 | float |
%fd | .f64 | double |
%h | .f16 / .b16 | half |
%p | .pred | predicate |
%rs | .b16 | short |
compiler 생성 PTX의 관례. inline asm은 자유.
| sreg | 대응 |
|---|---|
%tid.{x,y,z} | threadIdx |
%ntid.{x,y,z} | blockDim |
%ctaid.{x,y,z} | blockIdx |
%nctaid.{x,y,z} | gridDim |
%laneid | lane 0~31 |
%warpid | SM내 warp |
%smid | SM 번호 |
%clock %clock64 | cycle counter |
%warpid, %smid는 volatile. 스케줄러가 warp를 이동시키므로 안정적 식별자 아님.
| space | 범위 | 접근 명령 |
|---|---|---|
.reg | thread | 직접 피연산자 |
.sreg | thread (RO) | special reg |
.param | kernel / func | ld.param |
.local | thread | ld.local/st.local |
.shared | CTA | ld.shared/st.shared |
.global | device | ld.global/st.global |
.const | device (RO) | ld.const |
.tex | device (RO) | tex (legacy) |
.generic | 통합 주소 | ld / st (no suffix) |
| state space | physical | latency |
|---|---|---|
| .reg | RF 64K×32b/SM | 0 cyc |
| .shared | SMEM 192KB/SM | ~20 cyc |
| .local | HBM (per-thr) | ~400 cyc |
| .global | HBM | ~400 cyc |
| .const | const cache | ~4 cyc (hit) |
| .param | const bank | ~4 cyc |
A100 기준. 정확 수치 ↗ V02 §14
.param은 ABI에 따라 stack 또는 regld.param.u64 %rd1, [mykernel_param_0];
cvta.to.global.u64 %rd2, %rd1;
ld.global.f32 %f1, [%rd2];
// shared → generic cvta.shared.u64 %rd_gen, %rd_sh; // generic → global cvta.to.global.u64 %rd_g, %rd_gen;
.shared .align 16 .b8
smem_buf[16384];
// dynamic (extern shmem)
.extern .shared .align 16
.b8 smem_dyn[];
__shared__ float buf[N] 대응extern __shared__ ..., 런치 3번째 인자로 크기.local은 이름과 달리 HBM. register spill이 이곳으로 가면 latency ×20 ↑. 컴파일러의 "register pressure too high" 경고는 이걸 의미.
| op | 형태 | 비고 |
|---|---|---|
add.s32 | d = a + b | wrap |
add.sat.s32 | saturate | clamp |
add.cc.s32 | CC.CF set | multi-word |
mul.lo.s32 | 하위 32b | wrap 허용 |
mul.hi.s32 | 상위 32b | 64-bit 확장 |
mul.wide.s32 | → .s64 | 확장 결과 |
mad.lo.s32 | a·b + c | IMAD 대응 |
div.s32 | 비싸다 | ~수십 cyc |
rem.s32 | 비싸다 | div+mul |
| op | 의미 |
|---|---|
add.f32 | a + b |
mul.f32 | a · b |
fma.rn.f32 | a·b + c (1 rounding) |
sub.f32 | a − b |
neg.f32 | −a |
abs.f32 | |a| |
min.f32 max.f32 | 순서 |
rcp.rn.f32 | 1/a (근사) |
sqrt.rn.f32 | root |
rsqrt.approx.f32 | 1/√a (fast) |
| suffix | mode |
|---|---|
.rn | round to nearest even (IEEE) |
.rz | round toward zero (truncate) |
.rm | round toward −∞ |
.rp | round toward +∞ |
fma/mul/add는 rounding 필수.ftz: flush subnormal to zero.sat: clamp to [0,1] (f32) 또는 type range| op | 의미 |
|---|---|
and.b32 | bitwise AND |
or.b32 xor.b32 | OR / XOR |
not.b32 | ~a |
shl.b32 | left shift |
shr.s32 shr.u32 | signed / unsigned |
popc.b32 | popcount |
clz.b32 bfind.u32 | count leading zero / find MSB |
brev.b32 | bit reverse |
// compare setp.lt.s32 %p1, %r1, %r2; // select: %r3 = %p1 ? %r4 : %r5 selp.s32 %r3, %r4, %r5, %p1; // predicated @%p1 add.s32 %r3, %r1, %r2;
| cmp suffix | 의미 |
|---|---|
eq ne | =, ≠ |
lt le gt ge | < ≤ > ≥ |
lo ls hi hs | unsigned ver |
equ neu ltu … | float unordered |
num nan | NaN test |
// f32 → s32 (truncate) cvt.rzi.s32.f32 %r, %f; // f16 → f32 cvt.f32.f16 %f, %h; // u32 → f32 (round-nearest) cvt.rn.f32.u32 %f, %r;
cvt.rn.f16x2.f32 (두 f32 → f16x2)mul.lo.s32는 wrap. overflow detect 필요 시 mul.wide 후 상위 비교. .sat은 float에만 meaningful.
ld.global.f32 %f1, [%rd1];
ld.global.v4.f32 {%f0,%f1,%f2,%f3}, [%rd1];
ld.global.nc.f32 %f1, [%rd1]; // non-coherent
st.global.wt.f32 [%rd1], %f1; // write-through
st.shared.v2.b32 [%rs1], {%r0,%r1};
| mod | 의미 |
|---|---|
.ca | cache all (L1+L2) · default |
.cg | cache at global (L2 only, L1 bypass) |
.cs | cache streaming (evict first) |
.lu | last use (discard after) |
.cv | cache volatile (re-fetch) |
.nc | non-coherent (read-only cache) |
sm_80+: .nc는 __ldg intrinsic 대응
| mod | 의미 |
|---|---|
.wb | write-back (default) |
.cg | cache global (L2만) |
.cs | streaming |
.wt | write-through (L2 통과) |
// 128-bit load (4 × f32) ld.global.v4.f32 {%f0,%f1,%f2,%f3}, [%rd1]; // 128-bit load (2 × f64) ld.global.v2.f64 {%fd0,%fd1}, [%rd1]; // 64-bit load ld.global.v2.f32 {%f0,%f1}, [%rd1];
| type / vec | align (byte) |
|---|---|
| .b8 / .u8 / .s8 | 1 |
| .b16 / .f16 | 2 |
| .b32 / .f32 | 4 |
| .v2.b32 | 8 |
| .v4.b32 / .v2.b64 | 16 |
| .b128 | 16 |
misaligned → 여러 transaction으로 분해, BW 손실
ld.volatile.global: 최적화 불가, cache bypass 의도ld.weak: relaxed consistency 명시 (PTX 6+)ld는 relaxed (acquire/release 아님)ld.acquire / fence 병행 ↗ §12ld.nc(non-coherent)는 kernel 내에서 write가 있는 메모리에 쓰면 stale data. read-only 입력에만 사용.
ld.global.cg는 Ampere에서 L1 bypass. working set > L1 인 대량 스트리밍에 적합, small reuse에는 부적합.
ld.global → reg → st.shared (2-hop)cp.async (1-hop, DMA-like)// 16B copy, cache global (L1 bypass) cp.async.cg.shared.global [smem_addr], [gmem_addr], 16; // 8B copy, cache all cp.async.ca.shared.global [smem_addr], [gmem_addr], 8; // 4B copy cp.async.ca.shared.global [smem_addr], [gmem_addr], 4;
.ca: L1+L2 · .cg: L2만 (큰 복사에 유리)// 현재까지 issue한 cp.async들을 // 하나의 async-group으로 묶음 cp.async.commit_group; // 가장 오래된 그룹부터 최대 N개만 남기고 대기 cp.async.wait_group N; // 모든 그룹 완료 대기 cp.async.wait_all;
| size | .ca | .cg | 용도 |
|---|---|---|---|
| 4B | ✓ | ✓ | int / f32 |
| 8B | ✓ | ✓ | f64 / v2.f32 |
| 16B | ✓ | ✓ ★ | v4.f32 · 권장 |
warp 32 × 16B = 512B/issue, 128B 트랜잭션 4개로 퍼짐
// STAGES = 2: double buffer // STAGES = 3~: multi-stage const S = 3; .shared .align 16 .b8 sA[S][TILE_BYTES]; .shared .align 16 .b8 sB[S][TILE_BYTES]; // prologue: S-1 stage prefetch for (s = 0; s < S-1; ++s) { cp_async_tile(sA[s], gA[s]); cp_async_tile(sB[s], gB[s]); cp.async.commit_group; } // main loop for (k = 0; k < K_TILES; ++k) { // issue next stage (overlap) if (k + S - 1 < K_TILES) { cp_async_tile(sA[(k+S-1) % S], gA[k+S-1]); cp_async_tile(sB[(k+S-1) % S], gB[k+S-1]); } cp.async.commit_group; // wait so [k] is ready cp.async.wait_group S-2; bar.sync 0; // __syncthreads // compute on stage k ldmatrix(rA, sA[k % S]); ldmatrix(rB, sB[k % S]); mma.sync(rD, rA, rB, rD); } // epilogue cp.async.wait_all; bar.sync 0;
commit_group 빠뜨리면 wait가 영원히 풀리지 않음. ②__syncthreads는 in-flight async를 기다리지 않음. ③ byte size ≠ 4/8/16 이면 illegal.
.sync는 warp 내 barrier 포함.
mma.sync.aligned.m16n8k16.row.col
.f32.f16.f16.f32
{%f0,%f1,%f2,%f3}, // D (f32 × 4)
{%r0,%r1,%r2,%r3}, // A (f16 × 8)
{%r4,%r5}, // B (f16 × 4)
{%f4,%f5,%f6,%f7}; // C (f32 × 4)
A100 throughput: 1 mma / cycle / SM (FP16 m16n8k16)
| shape | A | B | C/D | 비고 |
|---|---|---|---|---|
| m16n8k8 | f16 | f16 | f16 | Volta-style |
| m16n8k8 | f16 | f16 | f32 | mixed |
| m16n8k16 | f16 | f16 | f16 | Ampere std |
| m16n8k16 | f16 | f16 | f32 | ★ DL 기본 |
| m16n8k16 | bf16 | bf16 | f32 | ★ BF16 training |
| m16n8k8 | tf32 | tf32 | f32 | ★ FP32 acc |
| m16n8k16 | s8 | s8 | s32 | INT8 |
| m16n8k16 | u8 | u8 | s32 | UINT8 |
| m16n8k32 | s8 | s8 | s32 | larger K |
| m16n8k32 | u8 | s8 | s32 | mixed sign |
| m16n8k64 | s4 | s4 | s32 | INT4 |
| m16n8k128 | b1 | b1 | s32 | binary (xor.popc) |
| m8n8k4 | f64 | f64 | f64 | FP64 TC |
.row.col: A row-major, B col-major (standard). 다른 조합은 데이터 재배치 필요.
A: 16 × 16 (row-major, f16)
thread t 의 소유:
row = (t / 4) (0..7)
col = (t % 4) * 2 + j (j=0,1)
plus row+8 행의 같은 col
→ t당 8 × f16 = 4 × .b32
B: 16 × 8 (col-major, f16)
row = (t / 4)
col = (t % 4) * 2 + j
→ t당 4 × f16 = 2 × .b32
D: 16 × 8 (row-major, f32)
row = (t / 4), col = (t%4)*2 + j
plus row+8
→ t당 4 × f32
.sync = warp 내 암묵적 barrier (activemask 기준).aligned = 모든 32 thread가 같은 instruction 도달 필수ldmatrix가 바로 이 layout에 맞춰 로드 ↗ §8.
// x4: 4 fragment 한 번에 ldmatrix.sync.aligned.x4.m8n8.shared.b16 {%r0,%r1,%r2,%r3}, [%rs_base]; // x4 + transpose (B matrix용) ldmatrix.sync.aligned.x4.trans .m8n8.shared.b16 {%r0,%r1,%r2,%r3}, [%rs_base]; // x2 (A: 16x16 = 2 x 8x8) ldmatrix.sync.aligned.x2.m8n8.shared.b16 {%r0,%r1}, [%rs_base];
x1 (1 fragment, 8 rows): thread 0~7 : row 0~7 base 제공 thread 8~31 : 주소 무시 x2 (2 fragment, 16 rows): thread 0~7 : frag0 row 0~7 thread 8~15 : frag1 row 0~7 thread 16~31 : 무시 x4 (4 fragment, 32 rows): thread 0~7 : frag0 thread 8~15 : frag1 thread 16~23 : frag2 thread 24~31 : frag3
| variant | t당 결과 | 용도 |
|---|---|---|
| x1 | .b32 × 1 | 8×8 한 조각 |
| x2 | .b32 × 2 | 16×8 A 반쪽 |
| x4 | .b32 × 4 | A full 16×16 |
| x2.trans | .b32 × 2 | B 전치 load |
| x4.trans | .b32 × 4 | B full |
ldmatrix.x4 → A(16×16 f16) 4개 fragmentldmatrix.x2.trans → B(16×8 f16) 2개 fragment// XOR swizzle (8-way) col_swz = col ^ ((row & 7) << 3); // 쓸 때와 읽을 때 같은 swizzle // → ldmatrix가 conflict 없이 읽음
CUTLASS의 SmemLayoutSwizzle 참고. 상세 ↗ V06 §9.
// A 16x16 f16 → fragment 4개 // B 16x8 f16 → fragment 2개 (trans) ldmatrix.x4 {a0,a1,a2,a3}, [sA]; ldmatrix.x2.trans {b0,b1}, [sB]; mma.sync.m16n8k16.row.col .f32.f16.f16.f32 {d0,d1,d2,d3}, {a0,a1,a2,a3}, {b0,b1}, {d0,d1,d2,d3};
%laneid로 분기.
| mode | C intrinsic | 의미 |
|---|---|---|
.idx | __shfl_sync | 임의 src lane |
.up | __shfl_up_sync | lane − delta |
.down | __shfl_down_sync | lane + delta |
.bfly | __shfl_xor_sync | lane ^ mask |
shfl.sync.down.b32 %r_out | %p_valid, %r_in, 0x10, 0x1f, 0xffffffff; // delta=16, clamp=31, mask=full
| op | 결과 |
|---|---|
vote.all.pred | 모두 true면 1 |
vote.any.pred | 하나라도 true면 1 |
vote.uni.pred | 모두 동일하면 1 |
vote.ballot.b32 | 32-bit mask |
vote.sync.ballot.b32 %r_mask, %p_cond, 0xffffffff; // == __ballot_sync(0xff..., cond)
activemask.b32 %r_mask; // 현재 실행 중인 lane bitmap // == __activemask()
shfl.sync의 membermask로 사용// 같은 값 가진 lane mask
match.any.sync.b32 %r_m, %r_v, %mask;
match.all.sync.b32 %r_m | %p,
%r_v, %mask;
0xffffffff: 전체 32 laneactivemask 결과 사용// sum reduction for (d = 16; d > 0; d >>= 1) { shfl.sync.bfly.b32 %t|%p, %sum, %d, 0x1f, 0xffffffff; @%p add.f32 %sum, %sum, %t; } // lane 0 에 총합
5 단계 = log₂32, 총 32개 값 합산
shfl.b32 (no .sync) 는 Volta 이전 전용. sm_70+ 에서는 항상 .sync + membermask 사용.
setp.lt.s32 %p1, %r1, %r2; @%p1 add.s32 %r3, %r1, %r2; @!%p1 sub.s32 %r3, %r1, %r2;
| 시나리오 | 선택 |
|---|---|
| 양 분기 모두 짧음 | predication |
| 한 분기 매우 김 | branch |
| 분기 비용 > 양쪽 실행 비용 | predication |
| lane 모두 같은 경로 | branch (uniform) |
ptxas가 heuristic으로 선택. -maxrregcount·__builtin_expect 영향.
| instr | 의미 |
|---|---|
bra LBL | unconditional |
@p bra LBL | predicated |
bra.uni LBL | warp-uniform 선언 |
call .func | device func call |
ret | return |
exit | thread 종료 |
trap | fatal abort |
brkpt | debugger halt |
__syncwarp / bar.warp.sync 사용.
// 권장 패턴 (divergent region) @%p bra TAKEN; // not-taken path bra DONE; TAKEN: // taken path DONE: bar.warp.sync 0xffffffff; // reconverge 명시
// boundary guard: thread 단위 early exit setp.ge.s32 %p, %tid, %N; @%p exit; // 전 lane이 exit 하면 warp 제거 // 일부만 exit → 남은 lane만 진행
exit: thread 종료, warp 수축ret: 함수 복귀ret = kernel 종료bra.uni: 모든 lane이 같은 target임을 컴파일러가 보장@p bra.uni: 같은 predicate 값을 모두 가짐 전제.branchtargets L0, L1, L2;
bra.sync LB, %r_idx;
// indirect branch
if (laneIdx < 8) 같은 lane-partial은 branch가 유리한 경우 많음.
atom.global.add.u32 %r_old, [%rd_addr], %r_val; atom.global.cas.b32 %r_old, [%rd_addr], %r_cmp, %r_new; atom.shared.max.s32 %r_old, [%rs_addr], %r_val;
| op | 의미 | type |
|---|---|---|
add | *a += v | u32 s32 u64 s64 f32 f64 f16 bf16 |
cas | compare-and-swap | b32 b64 b16 |
exch | *a ← v | b32 b64 |
min / max | clamp | u32 s32 u64 s64 (f*: sm_90) |
and / or / xor | 비트 | b32 b64 |
inc / dec | wrap count | u32 |
atom = old value 반환. red = old value 버림 (reduction). red가 가볍다.
atom.global.add.u32 %r_old, [%rd], %r_val; // vs red.global.add.u32 [%rd], %r_val; // no dst
red 선호atom (old 필요)| scope | 범위 |
|---|---|
.cta | CTA 내부 (shmem 범위) |
.gpu | 동일 GPU의 모든 CTA |
.sys | 전체 시스템 (CPU · peer GPU 포함) |
.cluster | cluster 내부 (sm_90+) ↗ V04 §2 |
scope가 좁을수록 latency / trafic ↓
// lock-free update template LOOP: ld.global.u32 %old, [%rd]; // compute new from old ... atom.global.cas.b32 %cur, [%rd], %old, %new; setp.ne.b32 %p, %cur, %old; @%p bra LOOP;
| suffix | 의미 |
|---|---|
.relaxed | 순서 보장 없음 (default) |
.acquire | 이후 op는 이 뒤 |
.release | 이전 op는 이 앞 |
.acq_rel | 양쪽 |
atom.acq_rel.global.add.u32 %r_old, [%rd], %r_val;
atomicAdd(float*)는 FP32에서 sm_20+, FP64에서 sm_60+ 지원. FP16 atomic은 subnormal·NaN 처리 조건 확인. 상세 ↗ V09 §4.
| instr | 세대 | 의미 |
|---|---|---|
membar.cta | 전통 | CTA 내 memory ordering |
membar.gl | 전통 | GPU 전역 |
membar.sys | 전통 | system-wide |
fence.acq_rel.cta | sm_70+ | 모델 기반 acq_rel |
fence.acq_rel.gpu | sm_70+ | GPU scope |
fence.acq_rel.sys | sm_70+ | system scope |
fence.sc.cta | sm_70+ | seq-consistent |
fence.sc.gpu | sm_70+ | sc GPU |
fence.sc.sys | sm_70+ | sc system |
fence는 memory consistency 모델 공식화(PTX 6+).
| instr | 의미 |
|---|---|
bar.sync 0 | __syncthreads() |
bar.sync N, cnt | partial barrier (N: 0~15) |
bar.arrive N, cnt | arrive only (non-blocking) |
bar.red.and.pred | barrier + vote |
bar.warp.sync mask | warp 내 재수렴 |
barrier.sync 0 | 바뀐 이름(PTX 7.0+) |
16개 named barrier: 0~15. CTA 내 역할 분담 가능.
// Producer st.global.u32 [buf], %data; fence.release.gpu; st.global.u32 [flag], 1; // Consumer LOOP: ld.global.u32 %f, [flag]; setp.eq.u32 %p, %f, 0; @%p bra LOOP; fence.acquire.gpu; ld.global.u32 %data, [buf];
relaxed ld로 spin. acquire/release로 순서 보장.
| 통신 상대 | scope |
|---|---|
| 같은 CTA 내 thread | .cta |
| 다른 CTA 같은 GPU | .gpu |
| CPU / peer GPU | .sys |
| 같은 cluster (sm_90+) | .cluster |
__syncthreads() ≡ bar.sync 0__threadfence() ≡ membar.gl ≈ fence.sc.gpu__threadfence_block() ≡ membar.cta__threadfence_system() ≡ membar.sys__syncwarp(mask) ≡ bar.warp.sync maskbar.sync는 arrive count가 CTA 내 모든 thread여야 deadlock 안 남. divergent branch에서 일부만 도달하면 영원히 대기.
.relaxed.acquire · st.release 또는 fence| ordering | 적용 | 의미 |
|---|---|---|
.weak / .relaxed | ld st atom | 순서 보장 없음 |
.acquire | ld atom | 이후 op를 넘어 이동 금지 |
.release | st atom | 이전 op를 넘어 이동 금지 |
.acq_rel | atom fence | 양쪽 장벽 |
.sc (seq-cst) | fence | 전역 순서 존재 |
| scope \ ord | relaxed | acquire | release | acq_rel | sc |
|---|---|---|---|---|---|
| .cta | ld/st atom.relaxed.cta |
ld.acquire.cta atom.acquire.cta |
st.release.cta atom.release.cta |
atom.acq_rel.cta fence.acq_rel.cta |
fence.sc.cta |
| .gpu | atom.relaxed.gpu | ld.acquire.gpu atom.acquire.gpu |
st.release.gpu atom.release.gpu |
atom.acq_rel.gpu fence.acq_rel.gpu |
fence.sc.gpu |
| .sys | atom.relaxed.sys | ld.acquire.sys atom.acquire.sys |
st.release.sys atom.release.sys |
atom.acq_rel.sys fence.acq_rel.sys |
fence.sc.sys |
| .cluster sm_90 | atom.relaxed.cluster | ld.acquire.cluster | st.release.cluster | fence.acq_rel.cluster | fence.sc.cluster |
scope 확장 시 latency ↑ (cta < gpu < sys). 꼭 필요한 최소 scope 선택.
fence.sc.X는 전역 total order 생성. 모든 thread가 동일 interleaving 관찰.
비용 최대. 필요 최소로 사용.
| CUDA C++ | PTX |
|---|---|
memory_order_relaxed | .relaxed |
memory_order_acquire | .acquire |
memory_order_release | .release |
memory_order_acq_rel | .acq_rel |
memory_order_seq_cst | .sc |
thread_scope_block | .cta |
thread_scope_device | .gpu |
thread_scope_system | .sys |
ld.acquire.cta는 다른 CTA 간 동기화에 무력.
ld.global.nc + 수동 interpolation으로 대체| instr | 의미 |
|---|---|
tex.1d / .2d / .3d | sampling |
tex.a1d / .a2d | array |
tex.cube / .acube | cube map |
tld4 | gather 4 texel |
txq | query descriptor |
// 2D bilinear sample tex.2d.v4.f32.f32 {%f0,%f1,%f2,%f3}, [%rd_tex, {%f_u, %f_v}]; // gather (R channel, 4 texel) tld4.r.2d.v4.f32.f32 {%f0,%f1,%f2,%f3}, [%rd_tex, {%f_u,%f_v}];
| instr | 의미 |
|---|---|
suld.b | surface load (bypass) |
sust.b | surface store |
suld.p | format-converted load |
sured | surface reduction |
surface = 2D/3D array 이미지. texture와 달리 write 가능.
// CUDA C: cudaTextureObject_t tex.2d.v4.f32.f32 {%f0,%f1,%f2,%f3}, [%rd_texobj, {%f_u,%f_v}]; // register에 handle 보유 → 함수 전달 OK
ld.global.nc + manual interpolation이 일반적.
asm volatile( "mov.u32 %0, %%laneid;" : "=r"(lane));
| 문자 | 대응 |
|---|---|
"r" | .u32 / .s32 / .b32 register |
"l" | .u64 / .s64 / .b64 register |
"f" | .f32 |
"d" | .f64 |
"h" | .u16 / .b16 |
"n" | compile-time immediate |
"=" | output prefix |
"+" | in-out |
// ldmatrix 인라인 asm volatile( "ldmatrix.sync.aligned.x4.m8n8" ".shared.b16 " "{%0,%1,%2,%3}, [%4];" : "=r"(x0), "=r"(x1), "=r"(x2), "=r"(x3) : "r"(smem_addr));
%%는 실제 % 이스케이프asm()을 다른 op와 reorder/elide 할 수 있음. volatile은 이동·제거 금지를 명시.
// generic → .shared smem_addr uint32_t smem_addr; asm("cvta.to.shared.u64 %0, %1;" : "=l"(tmp64) : "l"(ptr)); asm("cvt.u32.u64 %0, %1;" : "=r"(smem_addr) : "l"(tmp64));
ldmatrix / cp.async에 shared addr은 32-bit 필요.
asm volatile( "mma.sync.aligned.m16n8k16" ".row.col.f32.f16.f16.f32 " "{%0,%1,%2,%3}, " "{%4,%5,%6,%7}, " "{%8,%9}, " "{%10,%11,%12,%13};" : "=f"(d0), "=f"(d1), "=f"(d2), "=f"(d3) : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(b0), "r"(b1), "f"(c0), "f"(c1), "f"(c2), "f"(c3));
f16 fragment는 .b32에 2개 pack → "r" constraint 사용.
asm volatile( "cp.async.cg.shared.global " "[%0], [%1], 16;" :: "r"(smem_addr), "l"(gmem_ptr)); asm volatile( "cp.async.commit_group;"); asm volatile( "cp.async.wait_group %0;" :: "n"(STAGES-2));
wait_group의 count는 immediate (n constraint).
nvcc -Xptxas -v로 상세 출력.
| 옵션 | 효과 |
|---|---|
-ptx | .ptx 만 생성 |
-cubin | .cubin (SASS) 생성 |
-keep | 중간 파일 유지 |
-lineinfo | source line 매핑 |
--generate-line-info | 위와 동일 |
-src-in-ptx | PTX에 .cu 소스 삽입 |
-Xptxas -v | reg / smem 사용량 출력 |
-Xptxas -O3 | ptxas 최적화 레벨 |
# .cu → .ptx 만 보기 nvcc -O3 -arch=sm_80 \ -src-in-ptx -lineinfo \ -ptx kernel.cu -o kernel.ptx # reg/smem 사용량 nvcc -arch=sm_80 -Xptxas -v \ -c kernel.cu # 전 중간 파일 유지 nvcc -arch=sm_80 -keep kernel.cu
// 예시 출력 ptxas info : Compiling entry function '_Z6kerneli' for 'sm_80' ptxas info : Function properties for _Z6kerneli 0 bytes stack frame, 0 bytes spill stores, 0 bytes spill loads ptxas info : Used 32 registers, 4096 bytes smem, 376 bytes cmem[0]
_Z로 시작.
kernel<float, 256> → _Z6kernelIfLi256EEvPKT_Pi
c++filt -p _Z...로 역변환extern "C" 선언 시 mangling 제거.entry 찾기 → target kernel 확인.param 선언 → argument layout.reg 선언 → 사용된 register 종류/수mma / cp.async / ld.global 비율.local 언급 / st.local| hint | PTX에 미치는 영향 |
|---|---|
__launch_bounds__ | .maxntid 생성 |
#pragma unroll N | loop 전개 |
__restrict__ | alias 제거 → reorder 가능 |
__forceinline__ | device 함수 inline |
__noinline__ | call 유지 |
-maxrregcount N | reg 상한 강제 |
grep mma.sync kernel.ptx · grep cp.async kernel.ptx로 확인. 누락 시 -arch 버전 부족 가능성.
| instr | 한 줄 요약 |
|---|---|
ld.global.f32 | HBM → reg f32 |
ld.global.v4.f32 | 128-bit vector load |
ld.global.nc.f32 | read-only cache |
st.global.f32 | reg → HBM |
ld.shared.f32 | SMEM → reg |
st.shared.f32 | reg → SMEM |
cp.async.cg.shared.global | async copy (Ampere) |
cp.async.commit_group | async 그룹 마감 |
cp.async.wait_group N | async 대기 |
ldmatrix.x4 | SMEM → mma fragment |
mma.sync m16n8k16 | Tensor Core fma |
shfl.sync.down.b32 | warp reduction |
vote.sync.ballot.b32 | warp mask 생성 |
atom.global.add.u32 | HBM atomic |
fence.acq_rel.gpu | GPU-scope fence |
bar.sync 0 | __syncthreads |
bar.warp.sync 0xff.. | warp 재수렴 |
setp.lt.s32 | predicate 생성 |
@%p bra L | predicated branch |
ret | kernel exit |
| space | 한 글자 | 용도 |
|---|---|---|
| .reg | R | 레지스터 |
| .shared | S | CTA shmem |
| .global | G | HBM |
| .const | C | const bank |
| .local | L | thread-private HBM |
| .param | P | kernel arg |
.u/.s: unsigned/signed.b: opaque bit.f16/.bf16/.tf32/.f32/.f64: 부동.f16x2: packed half pair.pred: 1-bit predicate.sync: warp-wide barrier 포함.aligned: 모든 lane 동일 도달 보장.nc: non-coherent RO cache.cg: L1 bypass, L2만.volatile: 최적화 차단-ptx + -Xptxas -v