gpumode · 강의 아카이브
《GPU Mode》 L016 2024 · APR · 20 High priority transcript · available

On Hands Profiling

Lightning AI 의 Taylor Robbie 가 — “장표보다 직접 손에 잡히는 게 낫다” 는 입장으로 — 실제 모델을 nsys/Nsight Compute/PyTorch profiler 위에 펼쳐 어디가 비는지, 어떤 kernel 이 dominant 한지, 무엇이 진짜 actionable hint 인지를 보여주는 라이브 워크숍. NVTX 마커 추가, dynamic shape 의 함정, memory profiler 까지.

nsys system trace NVTX 마커 PyTorch profiler warp occupancy cutlass GEMM 진단 memory profile dynamic shape embedding kernel kernel naming convention
T
Speaker
Taylor Robbie
Lightning AI · profiling · runtime optimization
강의 번호
L016
스피커
Taylor Robbie
학습 우선순위
High · 정독
자료
slides/code 별도 없음 · 라이브 화면
§ 01강의가 풀려는 문제· why this lecture exists

“진단을 봤다 — 그래서 다음에 뭘 해야 하는가”

Profiling 강의의 흔한 함정 — 도구의 사용법은 가르치지만 “진단 결과를 받았을 때 무엇을 해야 하는가” 까지는 깊게 안 다룬다. Taylor 의 강의는 정확히 그 자리를 푼다. 실제 production 모델을 화면에 띄우고 — 어떤 metric 이 actionable 하고, 어떤 metric 이 그저 정보일 뿐인지를 라이브로 시연.

강의가 답하는 세 질문.

  1. “warp occupancy 가 30% 다” — 그게 나쁜가 좋은가, 그래서 어쩌라는 건가 같은 metric 의 해석.
  2. nsys 의 timeline 에서 어디를 먼저 보는가 — 진단의 시각적 우선순위.
  3. “fast kernel” 이라고 써 둔 게 사실 더 느릴 때 — 어떻게 발견하고 풀어가는가의 실전 디버깅 시퀀스.
강의의 인지적 frame

Taylor 의 입장이 명시적으로 “prescriptive” — “이 결과를 보면 다음에 무엇을 해라” 의 처방. profiling 은 도구가 아니라 의사결정 입력. 같은 nsys 화면을 두 사람이 보는데 한 명만 다음 step 을 안다면 — 도구의 문제가 아니라 frame 의 문제.

“profiler 가 데이터를 준다 — 그 위에 patten 을 올리는 건 사람의 일이다. 도구가 답을 주는 게 아니다.”Taylor Robbie · 강의 paraphrase
§ 023 도구의 자리· nsys · NCU · pytorch profiler

각 도구가 답하는 질문이 다르다 — 섞으면 안 된다

Taylor 가 강의 초반에 명확히 정리하는 자리 — 세 도구를 같은 “profiler” 라는 이름으로 묶으면 안 된다. 각자 다른 질문을 답한다.

nsys (Nsight Systems)system-level timeline

  • “iteration 안에 무엇이 무엇을 기다리는가”
  • CPU/GPU/NCCL/cuDNN 모두 한 timeline 위에.
  • kernel 의 internal 은 안 봄 — 이름과 시간만.
  • 가장 먼저 켜는 도구. 큰 그림.

NCU (Nsight Compute)kernel-level deep dive

  • “이 한 kernel 안에서 무엇이 막혔는가”
  • SM 별 occupancy, memory throughput, instruction stalls.
  • kernel 만 따로 봄 — system 컨텍스트 없음.
  • nsys 가 지목한 kernel 만 NCU 로 본다.

PyTorch profilerframework 인지

  • “어떤 PyTorch op 가 어떤 kernel 을 launch 했는가”
  • op-to-kernel mapping. autograd 정보.
  • Chrome trace 형식. 보통 nsys 의 부분집합.
  • memory snapshot, dynamic shape 정보 — 다른 도구가 못 줌.

고급 도구특수 자리

  • compute-sanitizer — race condition, OOB access.
  • Lightning Trainer profiler — Taylor 의 회사 제품. 학습 step 단위 모니터링.
  • tensorboard profile — nsys 의 web 등가물. 작은 모델에 충분.
cloud / 권한 함정

NCU 는 대부분의 cloud 환경에서 막혀 있다 — root 권한 또는 NVIDIA 의 “PerfWorks Compatibility Mode” 가 필요. 그래서 production 환경에서는 nsys + pytorch profiler 만 쓰는 경우가 많다. 자기 데스크탑이나 dedicated dev box 에서 NCU 로 다시 봐야 함.

§ 03NVTX 마커로 trace 가독성· 이름 붙이기

nsys timeline 위에 사용자가 직접 “여기는 forward, 여기는 backward” 라벨

Taylor 가 “라이브 워크숍의 첫 trick” 으로 보여준 것 — 어떤 trace 든 처음엔 의미가 안 잡힌다. NVTX 마커로 사용자 코드의 의미 단위를 timeline 위에 직접 그린다.

import torch.cuda.nvtx as nvtx

for i, batch in enumerate(loader):
    nvtx.range_push(f"step_{i}")

    nvtx.range_push("forward")
    out = model(batch)
    loss = criterion(out, target)
    nvtx.range_pop()

    nvtx.range_push("backward")
    loss.backward()
    nvtx.range_pop()

    nvtx.range_push("optimizer")
    optimizer.step()
    optimizer.zero_grad()
    nvtx.range_pop()

    nvtx.range_pop()  # step

nsys 로 launch:

nsys profile -t cuda,nvtx,osrt \
  --capture-range=cudaProfilerApi \
  -o trace python train.py

결과 — timeline 위에 “step_0”, “forward”, “backward”, “optimizer” 가 nested range 로 표시. iteration 안에서 어디가 비는지가 명확해진다.

PyTorch 가 자동으로 추가하는 NVTX 마커도 있다 — autograd 의 op 이름들. 하지만 사용자가 자기 모듈 단위로 한 단계 추가하는 게 가독성을 비약적으로 높인다.

FIG · NVTX 마커가 깔린 nsys timeline 형태한 training step
NVTX
step_0
NVTX (sub)
forward
backward
optimizer
CPU
launch
launch
launch
launch
launch
CUDA
gemm
act
gemm
idle
bw
gemm.bw
reduce
adam
NVTX 마커가 위 두 줄, CPU/CUDA 가 아래 두 줄. “forward 의 끝과 backward 의 시작 사이에 GPU 가 비는 갭” 같은 패턴이 한눈에 — 이게 진단의 첫 step.
§ 04warp occupancy 의 함정· 바쁘다 ≠ 빠르다

“occupancy 30%” 가 무조건 나쁜 게 아니다 — 어떤 30% 인가가 중요

Taylor 가 가장 자주 마주치는 오해 — “warp occupancy 가 낮으니까 이게 병목” 이라고 단정 짓는 것. 사실 occupancy 와 throughput 은 직교한다. 낮은 occupancy 로도 peak throughput 이 나는 kernel 이 있고, 높은 occupancy 인데 throughput 이 안 나는 kernel 도 있다.

FIG · occupancy 와 throughput 의 4 가지 자리FA, GEMM, embedding
FlashAttentionregister-heavy
occ 35%
throughput peak
대형 GEMMwell-tuned
occ 75%
throughput peak
embedding (small)memory-bound
occ 85%
throughput 30% — bad
small reductionunder-filled SM
occ 18%
throughput 12% — bad
FA 는 register 가 바빠서 occupancy 가 낮은데 그게 정상. embedding 은 occupancy 는 높은데 HBM bandwidth 에 막혀서 throughput 이 안 남. occupancy 만 보면 잘못된 결론을 낸다.
prescriptive 진단

Taylor 의 처방 — “occupancy 와 함께 항상 throughput 을 본다”. NCU 의 “SM throughput” 또는 “Compute throughput” metric. 그 둘이 모두 낮으면 launch shape 문제, occupancy 만 낮고 throughput 은 충분하면 register 가 일하는 중 (FA 처럼), throughput 만 낮으면 memory-bound — 다른 처방.

“occupancy 는 ‘얼마나 많은 warp 이 SM 에 거주 중인가’ 일 뿐이다 — 거주 중이라고 일하는 건 아니다. 일은 throughput 이 알려준다.”Taylor Robbie · 강의 paraphrase
§ 05kernel name 읽기· cutlass · sm_80 · gemm

kernel 이름의 prefix/suffix 가 자체로 진단 정보다

trace 위에 뜨는 kernel 이름이 길고 흉하다 — 하지만 각 부분이 정보다. Taylor 가 각 부분을 어떻게 읽는지 라이브로 시연.

# 예 — fp16 GEMM kernel 한 자리
void cutlass::Kernel<
  cutlass_80_tensorop_f16_s16816gemm_f16_128x128_32x4_tn_align8
>(...);

각 부분의 의미.

  • cutlass — CUTLASS template 위에서 만들어진 kernel.
  • 80 — Ampere (SM 8.0) 용. 이게 자기 GPU 와 맞아야 한다. H100 위에서 80 prefix 면 fallback — 차선.
  • tensorop — Tensor Core 사용. (없으면 CUDA core 만 — 큰 손해)
  • f16_s16816 — fp16 input, MMA 모양 16×16×16.
  • 128x128_32x4 — CTA tile 128×128, K 축 32, num_stages 4.
  • tn — A 가 transposed (T), B 가 normal (N).
  • align8 — 8-element aligned load (8×16-bit = 128-bit vector).

이 한 줄에서 즉시 진단 가능한 것.

  • “H100 인데 sm80 fallback” → driver/build target 확인.
  • “tensorop 이 안 보임” → fp32 input 일 수도 있음. dtype cast 점검.
  • “align4” 처럼 작은 align → input pointer 가 align 안 됨. contiguous() 추가.
  • “128x128 인데 작은 batch” → tail effect. 더 작은 tile 의 kernel 이 나았을 수도.
embedding kernel 도

indexSelectLargeIndex, SmallIndexFunctor 같은 이름은 — embedding lookup 의 batch size 분기. “Small” 인지 “Large” 인지가 cross-over 점. Taylor 의 § 06 사례.

§ 062× 느린 “fast” kernel· embedding 디버그

“fast embedding 이 2× 느렸다” — Taylor 의 라이브 디버깅 사례

강의의 가장 인상적인 사례. Taylor 가 production 모델에서 “이 자리는 fast embedding kernel 을 쓰면 50 µs 정도다” 라고 baseline 을 알고 있었는데 — 자기 모델에서 쟀더니 100 µs. 같은 kernel 이 2× 느렸다. 어떻게 풀었나.

  1. nsys timeline 부터 — embedding 호출의 위치를 확인. 다른 op 와 overlap 가능한지, 또는 직렬화되었는지.
  2. kernel 이름 확인SmallIndexFunctor 이 아니라 indexSelectLargeIndex 가 떠 있었다. “이 size 에는 small 이 빨라야 하는데 왜 large 가 떠 있나”.
  3. PyTorch 내부 dispatch 추적 — embedding 의 dispatch 함수에 size threshold 가 있다. 그 threshold 를 넘어가면 large path. Taylor 의 입력은 그 threshold 정확히 위에 있었음.
  4. 처방 — input 의 chunk 를 약간 작게 잘라서 small path 로 떨어지게. 또는 padding 으로 large path 의 occupancy 를 채우게. 후자가 더 좋은 trade-off.
교훈

“fast” 라는 라벨이 붙은 kernel 도 입력 분포에 따라 잘못된 path 로 dispatch될 수 있다. 그게 actionable hint — “현재 kernel 이름” + “기대 kernel 이름” 의 차이가 진단의 단서.

“profiler 가 답을 주는 게 아니다 — 답이 나올 만한 자리를 보여줄 뿐이다. 그 자리에 mental model 이 있어야 디버깅이 끝난다.”Taylor Robbie · 강의 paraphrase
§ 07dynamic shape 의 cost· recompile · cudaMalloc

“seq_len 이 매 iteration 마다 다르다” — 그게 어디서 비용을 내는가

대형 모델의 inference 또는 일부 학습에서 — input shape 가 매번 다르다. Taylor 가 이런 dynamic shape workload 에서 자주 만나는 두 cost.

cost 1 — torch.compile 의 recompile. compile 의 cache 가 shape 별로 잡혀 있어서, 새 shape 가 들어오면 recompile. 1-2 초 stall. 처음 N iteration 만 그렇고 그 다음엔 cache hit — 하지만 production 의 처음 N 번이 critical.

해결torch.compile(dynamic=True) 또는 mark_dynamic 으로 일부 dim 을 dynamic 으로 표시. 컴파일러가 단일 generic 커널 생성. 속도는 약간 떨어지지만 recompile 안 일어남.

cost 2 — cudaMalloc 의 비용. 매 iteration 마다 새 size 의 buffer 가 필요. CUDA caching allocator (PyTorch 의 default) 가 이전 size 의 buffer 를 재사용 못 하면 새 cudaMalloc — 0.1-1 ms stall.

해결 — pre-allocated pool, torch.cuda.set_per_process_memory_fraction, 또는 caching_allocator_warmup. PyTorch profiler 의 memory section 으로 확인.

진단 방법

PyTorch profiler 에 profile_memory=Truerecord_shapes=True. 그 trace 안에 같은 op 가 다른 shape 로 매 iteration 마다 다시 나타나는지 확인. 같은 op 가 cache 된 형태면 OK.

§ 08memory profiler· peak vs steady

“OOM 이 났다” — 어떤 시점의 peak 가 문제인가

Memory 진단은 보통 두 형태 — steady state (학습이 도는 동안 일관된 점유) 와 peak (가장 높은 자리). PyTorch profiler 의 memory snapshot 기능이 둘을 분리해서 본다.

import torch.profiler as profiler
from torch.profiler import profile, record_function

# memory profile 켜기
with profile(profile_memory=True,
             record_shapes=True) as prof:
    for _ in range(5):
        out = model(batch)
        loss = criterion(out, label)
        loss.backward()
        optimizer.step()

# peak vs allocated 분리
print(prof.key_averages().table(
    sort_by="self_cuda_memory_usage",
    row_limit=10))

Taylor 가 강조한 디테일.

  • activation memory 는 backward 시점에 peak — forward 끝까지 모든 layer 의 activation 이 남아 있어야 backward 가 가능.
  • optimizer state 는 학습 시작 시 peak — Adam 은 m, v 의 두 부가 state 가 weight 와 같은 크기.
  • gradient + 작은 임시 buffer — 보통 작지만 큰 batch 에서 늘어남.

처방. activation 이 큰 자리 → gradient checkpointing (recompute). optimizer 가 큰 자리 → ZeRO 또는 8-bit Adam. fragment 가 큰 자리 → allocator 의 fragmentation 분석.

visualizer

PyTorch 2.1+ 의 torch.cuda.memory._record_memory_history 로 전체 memory trace 를 dump 한 뒤 memory viz 에 넣으면 — 시간별 alloc/free 를 시각적으로. peak 가 어떤 op 위에 있는지 정확히 보임.

§ 09flame chart vs icicle· CPU vs GPU lane

같은 데이터를 두 시각으로 — 위에서 아래 vs 아래에서 위

Taylor 가 강의에서 짚은 디테일 — chrome trace 의 flame chart 와 icicle plot 의 차이. 두 시각이 같은 정보를 거꾸로 보여준다.

flame chart — 함수 호출 stack 을 위로 쌓는다. 가장 많이 시간 쓰는 함수가 가장 넓은 막대로. CPU profiler 의 표준.

icicle plot — 같은 stack 을 위에서 아래로. CPU 코드와 GPU 코드를 같은 화면에 놓을 때 GPU 의 “이 kernel 이 어떤 CPU op 가 launch 한 결과인가” 의 부모-자식 관계를 표현.

chrome://tracing 의 PyTorch profiler 가 icicle 을 쓴다. 위쪽 lane 이 CPU, 아래쪽이 GPU. flow event 화살표가 CPU op 와 그것이 launch 한 GPU kernel 을 잇는다.

읽는 패턴

icicle 위에서 — (1) GPU lane 의 큰 kernel 부터 본다, (2) 그 kernel 의 flow source CPU op 를 따라 올라간다, (3) 그 CPU op 의 부모 stack 을 본다. “이 GPU 일은 어디서 왔는가”의 답을 한 번에. profiler 의 가장 강력한 사용 자리.

multi-SM 의 의미

GPU lane 은 “하나의 SM 의 timeline” 이 아니다. 모든 SM 이 한 kernel 을 같이 돌면 그 kernel 이 한 줄에 그려진다. multiple stream 이면 다른 줄. nsys 도 같은 방식.

§ 10기억할 메모와 코드· key takeaways · repo
3 도구의 분리
nsys (system) → NCU (kernel deep) → pytorch profiler (op-kernel mapping). 각자 다른 질문.
NVTX 마커
사용자가 직접 timeline 에 의미 단위 라벨. iteration / forward / backward 의 가독성을 비약적으로.
occupancy ⊥ throughput
두 metric 은 직교. 항상 같이 본다. FA 같은 register-heavy kernel 은 낮은 occ 가 정상.
kernel 이름 읽기
cutlass_80_tensorop_… 의 각 부분이 정보. arch fallback, tensor core 사용 여부, tile 모양, alignment 모두 이름 안에.
prescriptive frame
진단을 보면 항상 “다음에 무엇을 해야 하는가” 까지. metric 만 보고 멈추면 의미 없음.
dynamic shape cost
torch.compile recompile + cudaMalloc 두 자리. mark_dynamic 또는 pre-allocated pool 로 풀음.
memory peak vs steady
activation peak (backward), optimizer state peak (학습 시작). memory viz 로 시각화.
icicle reading
GPU 큰 kernel → flow source CPU op → 부모 stack. 한 화면에서 “어디서 왔는가” 답.
Slides 슬라이드 없음 — 라이브 화면 시연
Code repo 에 별도 sample 코드 없음

손에 새기기 — 실습 시퀀스

  1. NVTX 마커 추가 — 자기 train loop 에 step / forward / backward / optimizer 4 개 range. nsys 로 trace 떠서 timeline 가독성 변화 확인.
  2. warmup 후 steady-state 측정 — 처음 5 iteration 은 무시, 6-10 만 nsys capture. --capture-range 활용.
  3. kernel 이름 해석 연습 — 자기 모델의 nsys top kernel 5 개의 이름을 해석. arch · tensorop · tile 모양 직접 짚어보기.
  4. occupancy + throughput 동시 측정 — NCU 의 “SM Throughput” 과 “Achieved Occupancy” 을 같이 dump. 4 가지 자리 중 어디 있는지 분류.
  5. embedding kernel dispatch 추적 — Small vs Large path 가 어디서 나뉘는지 PyTorch source 안에서 grep. 자기 입력이 어느 path 로 가는지.
  6. dynamic shape stress test — 매 iteration 마다 다른 seq_len 의 input. recompile 회수 측정. mark_dynamic 적용 후 비교.
  7. memory viz 사용torch.cuda.memory._record_memory_history 로 dump 후 web viz. peak 가 어떤 op 위인지 확인.
§ 12열린 질문· open questions
  • NCU 의 cloud 권한 우회 — 강의 시점 (2024 April) 이후 일부 cloud (Lambda, Coreweave 등) 가 NCU 를 허용. 현재 시점의 가능 여부 확인 필요.
  • Hopper / Blackwell 의 새 metric — TMA, WGMMA 의 효과를 NCU 가 어떻게 보여주는지. § 04 의 occupancy 해석이 새 hardware 위에서 어떻게 변하는지.
  • memory viz 의 distributed 확장 — multi-GPU 환경에서 각 GPU 의 memory snapshot 을 합쳐 보는 방법. ZeRO 의 sharded state 진단.
  • compute-sanitizer 의 사용 사례 — 강의에서 짧게 언급. 어떤 종류의 버그를 잡는지 — race condition, OOB 외에.
  • tensorboard profile vs PyTorch profiler — 두 도구의 정확한 차이. tensorboard 가 production 에 적합한가.
  • NVTX 의 push/pop 구조 외 alternatives — context manager 형태 (PyTorch 의 record_function) 와 비교. overhead 차이.
  • warmup 의 정확한 횟수 — 5 가 안전한가 10 이 안전한가. CUDA context init, allocator warmup, JIT compile 의 각 자리.
검증 메모

이 노트의 § 04 timing 수치 (50 µs, 100 µs 등) 는 강의 paraphrase. 자기 GPU + 자기 모델에서 직접 측정해야 baseline. nsys 와 NCU 를 자기 데스크탑에서 한번씩 직접 돌리는 게 강의 학습의 마무리.

← Lecture 015 CUTLASS — kernel 깊이파기에서 system-level 진단으로 Lecture 017 → NCCL — Dan Johnson 이 깐 ring all-reduce 와 distributed 통신