AEGLOSLAB 엔비디아 기술 스택

제2부 CUDA 프로그래밍

2.13 프로파일링 I — Nsight Systems

1.10에서 종이와 연필로 "이 커널은 메모리 바운드인가"를 추정했다면, 프로파일링은 그 추정을 실측으로 확정한다. 도구는 둘 — Nsight Systems(nsys)는 시스템 전체를 시간축에 펼쳐 "어디서 시간이 새는가"를, Nsight Compute(ncu)는 커널 하나를 현미경으로 "왜 느린가"를 답한다. 이 장은 시스템 수준(nsys), 다음 장(2.14)이 커널 수준(ncu)이다.

2.13.1 이 장에서 배우는 것 / 선수 지식

배우는 것:

  • 언제 nsys를, 언제 ncu를 쓰는지 판단한다(시스템 vs 커널 병목).
  • nsys profile로 타임라인을 수집하고, 커널 갭·복사 직렬화·CPU 병목·오버랩 실패를 읽는다.
  • 측정 → 가설 → 수정 → 재측정 사이클의 첫 단계(시스템 병목 식별).
  • 워밍업·클럭 변동 함정.

선수 지식: 1.5(워프 stall), 1.8(병합), 1.10(루프라인·메모리 바운드), 2.10(스트림·오버랩). 커널 수준 진단은 2.14.

환경 주의(S-5): 도구 이름·CLI·섹션 명칭은 시점·버전 의존. nsys는 CUDA Toolkit 동봉. 아래 출력은 예시(개략)다. 정확한 옵션은 nsys --help·공식 문서(A.3).

2.13.2 왜 측정인가 — 추측의 비용

GPU 최적화에서 가장 비용이 큰 실수는 잘못된 병목을 고치는 것이다. 메모리 바운드 커널에 텐서코어를 갈아 넣어도 안 빨라진다(1.10). 코드만 보면 "FLOP 많으니 연산 바운드겠지"라는 추정이 자주 틀린다. 실제 병목은 비병합 접근·낮은 점유율·런치 오버헤드·복사 직렬화처럼 소스에 안 보이는 곳에 있다.

핵심 규율: 먼저 시간이 어디로 가는지 보고(nsys), 가장 비용이 큰 커널만 골라 왜 느린지 본다(ncu). 모든 커널을 ncu로 파고들 필요는 없다 — 전체 시간의 80%를 먹는 한두 커널이 보통 답이다.

2.13.3 두 도구, 두 척도

Nsight Systems (nsys)Nsight Compute (ncu)
척도시스템 전체, 시간축커널 하나, 하드웨어 카운터
답하는 질문"시간이 어디로 가나""이 커널은 왜 느린가 / 무엇에 바운드인가"
보는 것커널·복사 타임라인, CPU 스레드, API, NVTX, 갭·오버랩점유율, SOL, 메모리 차트, 루프라인, warp stall
오버헤드낮음(샘플링·트레이싱)높음 — 커널을 여러 번 replay(수십~수백 배 느려질 수 있음)
먼저?항상 먼저nsys로 핫 커널을 고른 다음

결정 규칙: 반복이 느림/GPU가 유휴인 듯/복사 의심/CPU 병목 → nsys. 특정 커널이 핫이고 왜 느린지/메모리·연산 바운드 판정 → ncu(2.14).

비엔비디아 정직: 프로파일링 사고법은 벤더 중립 — "타임라인으로 시스템 병목, 카운터로 커널 병목"은 AMD(rocprof·Omniperf)·인텔(VTune)에도 적용된다. 루프라인·점유율·산술 강도는 가속기 종류와 무관. 차이는 도구 이름·메트릭 명칭.

2.13.4 Nsight Systems — 타임라인으로 읽는 시스템 병목

2.13.4.1 무엇이 타임라인에 그려지나

nsys는 실행 시간을 가로축으로, 여러 레인(track) 을 세로로 쌓는다.

t → CPU (메인) api api api cudaMemcpy/Launch 호출 복사엔진 H2D D2H DMA 복사 (별도 엔진) SM (커널) 커널 실제 연산 ↑갭↑ ↑갭↑ 복사 끝나고 커널 시작 사이 GPU 유휴 커널 끝나고 D2H 시작 사이 — 동기화·CPU 거품

읽는 법: SM(커널) 레인이 비어 있는 구간 = GPU 유휴 시간이다. 그 갭이 왜 생겼는지 위·아래 레인에서 찾는다. 갭 직전이 복사면 "복사 대기", 갭에 CPU API 호출이 길게 이어져 있으면 "CPU 병목·동기화 거품", 갭 없이 커널이 빽빽하면 GPU는 잘 쓰는 중(그땐 ncu로 커널 내부, 2.14).

2.13.4.2 수집 — nsys profile

nsys profile -o report ./my_app --some-arg      # 기본: .nsys-rep 리포트 생성
# -t: 추적 대상(CUDA API·NVTX 주석·OS 런타임; cudnn,cublas 추가 가능) · --stats=true: 콘솔 요약
nsys profile \
  -t cuda,nvtx,osrt \
  -o report --stats=true \
  ./my_app

긴 실행은 -c cudaProfilerApi로 코드의 cudaProfilerStart/Stop 구간만, 또는 --capture-range/--delay로 워밍업 이후 정상 구간만 취한다(2.13.5). GUI로 report.nsys-rep를 열면 타임라인, CLI는 nsys stats report.nsys-rep로 표 요약.

2.13.4.3 콘솔 요약 읽기 (예시·개략)

[CUDA Kernel Summary]   (시간 내림차순)
 Time%   Total(ms)  Instances  Avg(us)   Name
 62.0      310.5       1000     310.5    fused_norm_kernel        ← 핫
 21.0      105.2       1000     105.2    gemm_tile_kernel
  9.0       45.1       1000      45.1    softmax_kernel

[CUDA Memory Operations]
 Time%   Total(ms)  Operation
 70.0      88.0      [CUDA memcpy HtoD]
 30.0      37.7      [CUDA memcpy DtoH]

해석: fused_norm_kernel이 GPU 커널 시간의 62% → ncu로 파고들 첫 후보(2.14). HtoD/DtoH 복사가 상당하면(특히 매 반복) 전송이 직렬화 중일 수 있으니 타임라인에서 복사·커널의 오버랩을 확인한다.

2.13.4.4 타임라인에서 찾는 4대 시스템 병목

시스템 병목타임라인 증상해법
① 커널 갭 (GPU 기아)SM 레인이 자주 빈다. 작은 커널 다발 사이 런치 오버헤드·CPU 거품융합·CUDA Graphs(2.11)
② 복사 직렬화복사 엔진과 SM이 번갈아만 일하고 안 겹침pinned + 비동기 복사 + 스트림 오버랩(2.10)
③ CPU 병목CPU 레인은 빽빽한데 SM 레인은 듬성. 데이터 준비·파이썬 오버헤드입력 파이프라인(3.8)
④ 동기화 거품cudaDeviceSynchronize·동기 복사·이벤트 대기가 긴 정체불필요한 동기화 제거, 비동기화

NVTX 주석(nvtxRangePush/Pop 또는 PyTorch torch.cuda.nvtx)을 코드에 넣으면 타임라인에 의미 있는 구간 이름("forward"·"backward"·"dataloader")이 찍혀 어느 단계가 기아를 일으키는지 단번에 보인다.

2.13.5 측정을 망치는 함정 (타이밍)

  • 워밍업 누락. 첫 커널 실행은 JIT 컴파일(PTX→SASS, 2.12)·캐시 콜드·라이브러리 핸들 초기화·CUDA 컨텍스트 생성으로 느리다. 첫 몇 회를 버리고 정상 상태를 측정. nsys는 --capture-range/-c cudaProfilerApi로.
  • 클럭 변동. GPU는 전력·온도에 따라 클럭을 높이고 내린다(boost·thermal throttle). 측정 중 흔들리면 같은 커널이 매번 다른 시간을 낸다. 재현 가능한 측정은 클럭 고정(nvidia-smi --lock-gpu-clocks, 권한 필요)하거나 여러 번 측정해 분산을 본다.
  • 시스템 병목을 커널 최적화로 풀려 함. nsys에서 GPU가 기아 상태인데(커널 갭·CPU 병목·복사 직렬화) 커널 내부만 파면 답이 안 나온다. GPU가 빽빽이 일하는 것이 먼저 — 그 다음 커널 효율. 순서는 항상 nsys → ncu.
  • Release 빌드·실제 입력으로 안 잼. 디버그 빌드(-G)는 최적화가 꺼져 SASS가 다르다. 초소형 입력은 캐시에 다 들어가 메모리 바운드를 못 드러낸다. 배포 빌드·실제 크기로.

2.13.6 중간 점검 (체크포인트)

  • [ ] nsys(시스템)와 ncu(커널) 중 무엇을 먼저 쓸지, 증상으로 판단한다.
  • [ ] 타임라인에서 SM 레인의 갭을 찾고, 갭 원인(복사 대기·CPU 병목·동기화)을 위·아래 레인에서 짚는다.
  • [ ] 콘솔 요약에서 핫 커널(시간 80%)을 골라 ncu 후보로 삼는다.
  • [ ] 워밍업·클럭 변동을 측정에서 통제한다.

자가 점검: 학습 한 스텝이 느린데 GPU 사용률이 낮다 → 무엇을 먼저? (nsys — GPU가 유휴인 시스템 병목 의심, 타임라인부터.)

2.13.7 흔한 함정

  • 추측으로 최적화 시작. 측정 없이 병목을 정하면 잘못된 곳을 고친다. nsys로 시간이 어디 가는지부터.
  • 모든 커널을 ncu로 파기. 80%를 먹는 한두 개만. 그 식별이 nsys의 역할.
  • NVTX 미사용으로 단계 식별 어려움. forward/backward/dataloader 주석을 넣으면 어느 단계가 기아를 일으키는지 단번에.

2.13.8 연습문제

연습 1 (도구 선택). 각 증상에 nsys·ncu·이벤트 타이밍 중 무엇을 먼저: (a) 학습 한 스텝이 느린데 GPU 사용률 낮음 (b) 이 커널이 핫인데 메모리 바운드인지 모름 (c) 방금 융합한 커널이 빨라졌는지만 빨리 확인 (d) 복사가 커널과 안 겹치는 듯.

정답

(a) nsys — GPU가 유휴인 시스템 병목, 타임라인부터. (b) ncu --set full — SOL·루프라인(2.14). (c) CUDA 이벤트 타이밍 — 절대 시간 차이만 낮은 비용으로. (d) nsys — 복사·SM 레인 오버랩은 타임라인에서.

연습 2 (타임라인 해석). nsys 타임라인에서 SM 레인이 작은 커널들 사이로 규칙적 빈틈, CPU 레인은 launch로 꽉 찼다. 진단명과 해법은?

정답

런치 오버헤드로 인한 GPU 기아(CPU-bound). 소형 커널 다발이라 CPU가 GPU를 못 따라간다. 해법: 커널 융합 또는 CUDA Graphs(2.11)로 launch 시퀀스를 한 번에.

연습 3 (워밍업 함정). 같은 커널을 100회 호출하며 (a) 1회차 (b) 2~100회 평균 시간을 비교하라. 1회차가 왜 느린가? 무엇을 버려야?

정답

1회차는 JIT 컴파일(2.12)·콜드 캐시·컨텍스트/핸들 초기화로 느리다. 앞 몇 회를 워밍업으로 버리고 정상 상태 평균을 보고. 1회차를 포함해 평균 내면 짧은 벤치에서 크게 왜곡된다.

2.13.9 요약 · 다음 장

  • 프로파일링은 측정 → 가설 → 수정 → 재측정 사이클. 추측으로 병목을 정하지 말 것.
  • nsys(Nsight Systems) 는 시간축으로 시스템 병목(커널 갭·복사 직렬화·CPU 병목·동기화 거품)을 보여준다. 항상 먼저, 핫 커널을 고른다.
  • SM 레인의 갭 = GPU 유휴 시간. 갭 원인을 위·아래 레인에서 찾고, NVTX로 단계를 식별한다.
  • 워밍업·클럭 변동을 통제하고, 시스템 병목을 커널 최적화로 풀려 하지 말 것.

다음 장: 2.14 프로파일링 II — Nsight Compute. nsys로 고른 핫 커널 하나를 SOL·메모리 차트·점유율·warp stall·루프라인으로 해부해 "무엇에 바운드인가"를 5분 안에 판정한다.