πŸ“š Mojo GPU λ””λ²„κΉ…μ˜ 핡심

GPU λ””λ²„κΉ…μ˜ 세계에 μ˜€μ‹  것을 ν™˜μ˜ν•©λ‹ˆλ‹€! Puzzle 1-8을 톡해 GPU ν”„λ‘œκ·Έλž˜λ° κ°œλ…μ„ λ°°μ› μœΌλ‹ˆ, 이제 λͺ¨λ“  GPU ν”„λ‘œκ·Έλž˜λ¨Έμ—κ²Œ κ°€μž₯ μ€‘μš”ν•œ κΈ°μˆ μ„ 배울 μ€€λΉ„κ°€ λ˜μ—ˆμŠ΅λ‹ˆλ‹€: λ¬Έμ œκ°€ λ°œμƒν–ˆμ„ λ•Œ λ””λ²„κΉ…ν•˜λŠ” 방법.

GPU 디버깅은 μ²˜μŒμ—λŠ” μ–΄λ €μ›Œ 보일 수 μžˆμŠ΅λ‹ˆλ‹€. 수천 개의 μŠ€λ ˆλ“œκ°€ λ³‘λ ¬λ‘œ μ‹€ν–‰λ˜κ³ , λ‹€μ–‘ν•œ λ©”λͺ¨λ¦¬ 곡간이 있으며, ν•˜λ“œμ›¨μ–΄λ³„ λ™μž‘λ„ 닀루어야 ν•©λ‹ˆλ‹€. ν•˜μ§€λ§Œ μ μ ˆν•œ 도ꡬ와 μ›Œν¬ν”Œλ‘œμš°λ§Œ 있으면 GPU μ½”λ“œ 디버깅도 μ²΄κ³„μ μœΌλ‘œ λ‹€λ£° 수 μžˆμŠ΅λ‹ˆλ‹€.

이 κ°€μ΄λ“œμ—μ„œλŠ” CPU 호슀트 μ½”λ“œ(GPU μž‘μ—…μ„ μ„€μ •ν•˜λŠ” λΆ€λΆ„)와 GPU 컀널 μ½”λ“œ(병렬 연산이 μ‹€ν–‰λ˜λŠ” λΆ€λΆ„) λͺ¨λ‘λ₯Ό λ””λ²„κΉ…ν•˜λŠ” 방법을 λ°°μ›λ‹ˆλ‹€. μ‹€μ œ 예제, μ‹€μ œ 디버거 좜λ ₯, 그리고 μ—¬λŸ¬λΆ„μ˜ ν”„λ‘œμ νŠΈμ— λ°”λ‘œ μ μš©ν•  수 μžˆλŠ” 단계별 μ›Œν¬ν”Œλ‘œμš°λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

μ°Έκ³ : λ‹€μŒ λ‚΄μš©μ€ λ²”μš© IDE ν˜Έν™˜μ„±μ„ μœ„ν•΄ λͺ…령쀄 디버깅에 μ΄ˆμ μ„ 맞μΆ₯λ‹ˆλ‹€. VS Code 디버깅을 μ„ ν˜Έν•œλ‹€λ©΄ Mojo 디버깅 λ¬Έμ„œμ—μ„œ VS Code μ „μš© μ„€μ •κ³Ό μ›Œν¬ν”Œλ‘œμš°λ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.

GPU 디버깅이 λ‹€λ₯Έ 이유

λ„κ΅¬λ‘œ λ“€μ–΄κ°€κΈ° 전에, GPU 디버깅이 νŠΉλ³„ν•œ 이유λ₯Ό μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€:

  • 전톡적인 CPU 디버깅: 단일 μŠ€λ ˆλ“œ, 순차 μ‹€ν–‰, λ‹¨μˆœν•œ λ©”λͺ¨λ¦¬ λͺ¨λΈ
  • GPU 디버깅: 수천 개의 μŠ€λ ˆλ“œ, 병렬 μ‹€ν–‰, μ—¬λŸ¬ λ©”λͺ¨λ¦¬ 곡간, 경쟁 μƒνƒœ

μ΄λŠ” λ‹€μŒμ„ ν•  수 μžˆλŠ” μ „λ¬Έ 도ꡬ가 ν•„μš”ν•˜λ‹€λŠ” μ˜λ―Έμž…λ‹ˆλ‹€:

  • μ„œλ‘œ λ‹€λ₯Έ GPU μŠ€λ ˆλ“œ κ°„ μ „ν™˜
  • μŠ€λ ˆλ“œλ³„ λ³€μˆ˜μ™€ λ©”λͺ¨λ¦¬ 검사
  • 병렬 μ‹€ν–‰μ˜ λ³΅μž‘μ„± 처리
  • CPU μ„€μ • μ½”λ“œμ™€ GPU 컀널 μ½”λ“œ λͺ¨λ‘ 디버깅

디버깅 도ꡬ λͺ¨μŒ

Mojo의 GPU 디버깅 κΈ°λŠ₯은 ν˜„μž¬ NVIDIA GPU둜 μ œν•œλ©λ‹ˆλ‹€. Mojo 디버깅 λ¬Έμ„œμ— λ”°λ₯΄λ©΄ Mojo νŒ¨ν‚€μ§€μ—λŠ” λ‹€μŒμ΄ ν¬ν•¨λ©λ‹ˆλ‹€:

  • CPU μΈ‘ 디버깅을 μœ„ν•œ Mojo ν”ŒλŸ¬κ·ΈμΈμ΄ ν¬ν•¨λœ LLDB 디버거
  • GPU 컀널 디버깅을 μœ„ν•œ CUDA-GDB 톡합
  • λ²”μš© IDE ν˜Έν™˜μ„±μ„ μœ„ν•œ mojo debugλ₯Ό ν†΅ν•œ λͺ…령쀄 μΈν„°νŽ˜μ΄μŠ€

GPU μ „μš© 디버깅에 λŒ€ν•΄μ„œλŠ” Mojo GPU 디버깅 κ°€μ΄λ“œμ—μ„œ μΆ”κ°€ 기술 μ„ΈλΆ€ 사항을 μ œκ³΅ν•©λ‹ˆλ‹€.

이 μ•„ν‚€ν…μ²˜λŠ” μ΅μˆ™ν•œ 디버깅 λͺ…령어와 GPU μ „μš© κΈ°λŠ₯, 두 κ°€μ§€ μž₯점을 λͺ¨λ‘ μ œκ³΅ν•©λ‹ˆλ‹€.

디버깅 μ›Œν¬ν”Œλ‘œμš°: λ¬Έμ œμ—μ„œ ν•΄κ²°κΉŒμ§€

GPU ν”„λ‘œκ·Έλž¨μ΄ ν¬λž˜μ‹œν•˜κ±°λ‚˜, 잘λͺ»λœ κ²°κ³Όλ₯Ό λ‚΄κ±°λ‚˜, μ˜ˆμƒμΉ˜ λͺ»ν•œ λ™μž‘μ„ ν•  λ•Œ λ‹€μŒμ˜ 체계적인 접근법을 λ”°λ₯΄μ„Έμš”:

  1. 디버깅을 μœ„ν•œ μ½”λ“œ μ€€λΉ„ (μ΅œμ ν™” λΉ„ν™œμ„±ν™”, 디버그 심볼 μΆ”κ°€)
  2. μ μ ˆν•œ 디버거 선택 (CPU 호슀트 μ½”λ“œ vs GPU 컀널 디버깅)
  3. μ „λž΅μ  브레이크포인트 μ„€μ • (λ¬Έμ œκ°€ μ˜μ‹¬λ˜λŠ” μœ„μΉ˜μ—)
  4. μ‹€ν–‰ 및 검사 (μ½”λ“œλ₯Ό λ‹¨κ³„λ³„λ‘œ μ‹€ν–‰ν•˜λ©° λ³€μˆ˜ 검사)
  5. νŒ¨ν„΄ 뢄석 (λ©”λͺ¨λ¦¬ μ ‘κ·Ό, μŠ€λ ˆλ“œ λ™μž‘, 경쟁 μƒνƒœ)

이 μ›Œν¬ν”Œλ‘œμš°λŠ” Puzzle 01의 κ°„λ‹¨ν•œ λ°°μ—΄ 연산이든 Puzzle 08의 λ³΅μž‘ν•œ 곡유 λ©”λͺ¨λ¦¬ μ½”λ“œλ“  상관없이 μž‘λ™ν•©λ‹ˆλ‹€.

Step 1: 디버깅을 μœ„ν•œ μ½”λ“œ μ€€λΉ„

πŸ₯‡ μ² μΉ™: μ΅œμ ν™”λœ μ½”λ“œλŠ” μ ˆλŒ€ λ””λ²„κΉ…ν•˜μ§€ λ§ˆμ„Έμš”. μ΅œμ ν™”λŠ” λͺ…λ Ήμ–΄ μˆœμ„œλ₯Ό λ°”κΎΈκ³ , λ³€μˆ˜λ₯Ό μ œκ±°ν•˜κ³ , ν•¨μˆ˜λ₯Ό μΈλΌμΈν™”ν•˜μ—¬ 디버깅을 거의 λΆˆκ°€λŠ₯ν•˜κ²Œ λ§Œλ“­λ‹ˆλ‹€.

디버그 μ •λ³΄λ‘œ λΉŒλ“œν•˜κΈ°

λ””λ²„κΉ…μš© Mojo ν”„λ‘œκ·Έλž¨μ„ λΉŒλ“œν•  λ•ŒλŠ” 항상 디버그 심볼을 ν¬ν•¨ν•˜μ„Έμš”:

# 전체 디버그 μ •λ³΄λ‘œ λΉŒλ“œ
mojo build -O0 -g your_program.mojo -o your_program_debug

이 ν”Œλž˜κ·Έλ“€μ΄ ν•˜λŠ” 일:

  • -O0: λͺ¨λ“  μ΅œμ ν™”λ₯Ό λΉ„ν™œμ„±ν™”ν•˜μ—¬ μ›λž˜ μ½”λ“œ ꡬ쑰λ₯Ό 보쑴
  • -g: 디버거가 λ¨Έμ‹  μ½”λ“œλ₯Ό Mojo μ†ŒμŠ€μ— λ§€ν•‘ν•  수 μžˆλ„λ‘ 디버그 심볼 포함
  • -o: μ‰¬μš΄ 식별을 μœ„ν•΄ λͺ…λͺ…λœ 좜λ ₯ 파일 생성

이것이 μ€‘μš”ν•œ 이유

디버그 심볼 μ—†μ΄λŠ” 디버깅 μ„Έμ…˜μ΄ μ΄λ ‡κ²Œ λ³΄μž…λ‹ˆλ‹€:

(lldb) print my_variable
error: use of undeclared identifier 'my_variable'

디버그 심볼이 있으면 λ‹€μŒκ³Ό 같이 λ©λ‹ˆλ‹€:

(lldb) print my_variable
(int) $0 = 42

Step 2: 디버깅 접근법 선택

μ—¬κΈ°μ„œ GPU 디버깅이 ν₯λ―Έλ‘œμ›Œμ§‘λ‹ˆλ‹€. λ„€ κ°€μ§€ λ‹€λ₯Έ μ‘°ν•© μ€‘μ—μ„œ 선택할 수 있으며, μ μ ˆν•œ 것을 κ³ λ₯΄λ©΄ μ‹œκ°„μ„ μ ˆμ•½ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

λ„€ κ°€μ§€ 디버깅 μ‘°ν•©

λΉ λ₯Έ μ°Έμ‘°:

# 1. μ†ŒμŠ€ + LLDB: μ†ŒμŠ€μ—μ„œ 직접 CPU 호슀트 μ½”λ“œ 디버깅
pixi run mojo debug your_gpu_program.mojo

# 2. μ†ŒμŠ€ + CUDA-GDB: μ†ŒμŠ€μ—μ„œ 직접 GPU 컀널 디버깅
pixi run mojo debug --cuda-gdb --break-on-launch your_gpu_program.mojo

# 3. λ°”μ΄λ„ˆλ¦¬ + LLDB: 미리 컴파일된 λ°”μ΄λ„ˆλ¦¬μ—μ„œ CPU 호슀트 μ½”λ“œ 디버깅
pixi run mojo build -O0 -g your_gpu_program.mojo -o your_program_debug
pixi run mojo debug your_program_debug

# 4. λ°”μ΄λ„ˆλ¦¬ + CUDA-GDB: 미리 컴파일된 λ°”μ΄λ„ˆλ¦¬μ—μ„œ GPU 컀널 디버깅
pixi run mojo debug --cuda-gdb --break-on-launch your_program_debug

각 접근법을 μ–Έμ œ μ‚¬μš©ν• κΉŒ

ν•™μŠ΅κ³Ό λΉ λ₯Έ μ‹€ν—˜μš©:

  • μ†ŒμŠ€ 디버깅 μ‚¬μš© - 별도 λΉŒλ“œ 단계가 ν•„μš” μ—†μ–΄ 더 λΉ λ₯΄κ²Œ 반볡 κ°€λŠ₯

본격적인 디버깅 μ„Έμ…˜μš©:

  • λ°”μ΄λ„ˆλ¦¬ 디버깅 μ‚¬μš© - 더 예츑 κ°€λŠ₯ν•˜κ³  κΉ”λ”ν•œ 디버거 좜λ ₯, λΉŒλ“œ ν”Œλž˜κ·Έλ₯Ό μ™„μ „νžˆ μ œμ–΄ κ°€λŠ₯ (-O0 -g둜 둜컬 λ³€μˆ˜ 보쑴)

CPU μΈ‘ 문제용 (버퍼 ν• λ‹Ή, 호슀트 λ©”λͺ¨λ¦¬, ν”„λ‘œκ·Έλž¨ 둜직):

  • LLDB λͺ¨λ“œ μ‚¬μš© - main() ν•¨μˆ˜μ™€ μ„€μ • μ½”λ“œ 디버깅에 적합

GPU 컀널 문제용 (μŠ€λ ˆλ“œ λ™μž‘, GPU λ©”λͺ¨λ¦¬, 컀널 ν¬λž˜μ‹œ):

  • CUDA-GDB λͺ¨λ“œ μ‚¬μš© - κ°œλ³„ GPU μŠ€λ ˆλ“œλ₯Ό κ²€μ‚¬ν•˜λŠ” μœ μΌν•œ 방법

μž₯점은 λ‹€μ–‘ν•˜κ²Œ μ‘°ν•©ν•΄μ„œ μ‚¬μš©ν•  수 μžˆλ‹€λŠ” μ μž…λ‹ˆλ‹€. μ†ŒμŠ€ + LLDB둜 μ„€μ • μ½”λ“œλ₯Ό λ””λ²„κΉ…ν•œ λ‹€μŒ, μ†ŒμŠ€ + CUDA-GDB둜 μ „ν™˜ν•΄μ„œ μ‹€μ œ 컀널을 디버깅할 수 μžˆμŠ΅λ‹ˆλ‹€.


CUDA-GDB둜 GPU 컀널 디버깅 μ΄ν•΄ν•˜κΈ°

이제 GPU 컀널 λ””λ²„κΉ…μž…λ‹ˆλ‹€ - 디버깅 도ꡬ λͺ¨μŒμ—μ„œ κ°€μž₯ κ°•λ ₯ν•˜λ©΄μ„œλ„ λ³΅μž‘ν•œ λΆ€λΆ„μž…λ‹ˆλ‹€.

--cuda-gdbλ₯Ό μ‚¬μš©ν•˜λ©΄ MojoλŠ” NVIDIA의 CUDA-GDB 디버거와 ν†΅ν•©λ©λ‹ˆλ‹€. 이것은 λ‹¨μˆœν•œ 디버거가 μ•„λ‹™λ‹ˆλ‹€ - GPU μ»΄ν“¨νŒ…μ˜ 병렬 λ©€ν‹°μŠ€λ ˆλ“œ 세계λ₯Ό μœ„ν•΄ νŠΉλ³„νžˆ μ„€κ³„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

CUDA-GDBκ°€ νŠΉλ³„ν•œ 이유

일반 GDBλŠ” ν•œ λ²ˆμ— ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œλ₯Ό λ””λ²„κΉ…ν•˜λ©° 순차 μ½”λ“œλ₯Ό λ‹¨κ³„λ³„λ‘œ μ‹€ν–‰ν•©λ‹ˆλ‹€. CUDA-GDBλŠ” 수천 개의 GPU μŠ€λ ˆλ“œλ₯Ό λ™μ‹œμ— λ””λ²„κΉ…ν•˜λ©°, 각각이 μ„œλ‘œ λ‹€λ₯Έ λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ΄λŠ” λ‹€μŒμ„ ν•  수 μžˆλ‹€λŠ” μ˜λ―Έμž…λ‹ˆλ‹€:

  • GPU 컀널 내뢀에 브레이크포인트 μ„€μ • - μ–΄λ–€ μŠ€λ ˆλ“œλ“  λΈŒλ ˆμ΄ν¬ν¬μΈνŠΈμ— λ„λ‹¬ν•˜λ©΄ 싀행을 μΌμ‹œ μ •μ§€
  • GPU μŠ€λ ˆλ“œ κ°„ μ „ν™˜ - 같은 μˆœκ°„μ— μ„œλ‘œ λ‹€λ₯Έ μŠ€λ ˆλ“œκ°€ 무엇을 ν•˜λŠ”μ§€ 검사
  • μŠ€λ ˆλ“œλ³„ 데이터 검사 - 같은 λ³€μˆ˜κ°€ μŠ€λ ˆλ“œλ§ˆλ‹€ λ‹€λ₯Έ 값을 κ°€μ§€λŠ” 것을 확인
  • λ©”λͺ¨λ¦¬ μ ‘κ·Ό νŒ¨ν„΄ 디버깅 - λ²”μœ„ 초과 μ ‘κ·Ό, 경쟁 μƒνƒœ, λ©”λͺ¨λ¦¬ 손상 포착 (이런 문제 감지에 λŒ€ν•΄μ„œλŠ” Puzzle 10μ—μ„œ 더 μžμ„Ένžˆ)
  • 병렬 μ‹€ν–‰ 뢄석 - μŠ€λ ˆλ“œλ“€μ΄ μ–΄λ–»κ²Œ μƒν˜Έμž‘μš©ν•˜κ³  λ™κΈ°ν™”ν•˜λŠ”μ§€ 이해

이전 퍼즐의 κ°œλ…κ³Ό μ—°κ²°

Puzzle 1-8μ—μ„œ 배운 GPU ν”„λ‘œκ·Έλž˜λ° κ°œλ…μ„ κΈ°μ–΅ν•˜μ‹œλ‚˜μš”? CUDA-GDB둜 λŸ°νƒ€μž„μ— λͺ¨λ“  것을 검사할 수 μžˆμŠ΅λ‹ˆλ‹€:

μŠ€λ ˆλ“œ 계측 ꡬ쑰 디버깅

Puzzle 1-8μ—μ„œ λ‹€μŒκ³Ό 같은 μ½”λ“œλ₯Ό μž‘μ„±ν–ˆμŠ΅λ‹ˆλ‹€:

# Puzzle 1μ—μ„œ: κΈ°λ³Έ μŠ€λ ˆλ“œ 인덱싱
i = thread_idx.x  # 각 μŠ€λ ˆλ“œκ°€ κ³ μœ ν•œ 인덱슀λ₯Ό μ–»μŒ

# Puzzle 7μ—μ„œ: 2D μŠ€λ ˆλ“œ 인덱싱
row = thread_idx.y  # 2D μŠ€λ ˆλ“œ κ·Έλ¦¬λ“œ
col = thread_idx.x

CUDA-GDB둜 이 μŠ€λ ˆλ“œ μ’Œν‘œλ“€μ΄ μ‹€μ œλ‘œ λ™μž‘ν•˜λŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€:

(cuda-gdb) info cuda threads

좜λ ₯:

  BlockIdx ThreadIdx To BlockIdx To ThreadIdx Count                 PC                                                       Filename  Line
Kernel 0
*  (0,0,0)   (0,0,0)     (0,0,0)      (3,0,0)     4 0x00007fffcf26fed0 /home/ubuntu/workspace/mojo-gpu-puzzles/solutions/p01/p01.mojo    13

그리고 νŠΉμ • μŠ€λ ˆλ“œλ‘œ μ΄λ™ν•΄μ„œ 무엇을 ν•˜λŠ”μ§€ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€:

(cuda-gdb) cuda thread (1,0,0)

좜λ ₯:

[Switching to CUDA thread (1,0,0)]

정말 κ°•λ ₯ν•œ κΈ°λŠ₯μž…λ‹ˆλ‹€ - 말 κ·ΈλŒ€λ‘œ 병렬 μ•Œκ³ λ¦¬μ¦˜μ΄ μ—¬λŸ¬ μŠ€λ ˆλ“œμ—μ„œ μ‹€ν–‰λ˜λŠ” 것을 직접 μ§€μΌœλ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

λ©”λͺ¨λ¦¬ 곡간 디버깅

λ‹€μ–‘ν•œ μœ ν˜•μ˜ GPU λ©”λͺ¨λ¦¬μ— λŒ€ν•΄ 배운 Puzzle 8을 κΈ°μ–΅ν•˜μ‹œλ‚˜μš”? CUDA-GDB둜 λͺ¨λ“  것을 검사할 수 μžˆμŠ΅λ‹ˆλ‹€:

# μ „μ—­ λ©”λͺ¨λ¦¬ 검사 (Puzzle 1-5의 λ°°μ—΄λ“€)
(cuda-gdb) print input_array[0]@4
$1 = {{1}, {2}, {3}, {4}}   # Mojo 슀칼라 ν˜•μ‹

# 둜컬 λ³€μˆ˜λ₯Ό μ‚¬μš©ν•΄ 곡유 λ©”λͺ¨λ¦¬ 검사 (thread_idx.xλŠ” μž‘λ™ν•˜μ§€ μ•ŠμŒ)
(cuda-gdb) print shared_data[i]   # thread_idx.x λŒ€μ‹  둜컬 λ³€μˆ˜ 'i' μ‚¬μš©
$2 = {42}

λ””λ²„κ±°λŠ” 각 μŠ€λ ˆλ“œκ°€ λ©”λͺ¨λ¦¬μ—μ„œ μ •ν™•νžˆ 무엇을 λ³΄λŠ”μ§€ λ³΄μ—¬μ€λ‹ˆλ‹€. μ΄λŠ” 경쟁 μƒνƒœλ‚˜ λ©”λͺ¨λ¦¬ μ ‘κ·Ό 버그λ₯Ό μž‘κΈ°μ— μ™„λ²½ν•©λ‹ˆλ‹€.

μ „λž΅μ  브레이크포인트 배치

CUDA-GDB λΈŒλ ˆμ΄ν¬ν¬μΈνŠΈλŠ” 병렬 μ‹€ν–‰κ³Ό ν•¨κ»˜ μž‘λ™ν•˜κΈ° λ•Œλ¬Έμ— 일반 λΈŒλ ˆμ΄ν¬ν¬μΈνŠΈλ³΄λ‹€ 훨씬 κ°•λ ₯ν•©λ‹ˆλ‹€:

# μ–΄λ–€ μŠ€λ ˆλ“œλ“  컀널에 μ§„μž…ν•  λ•Œ 쀑단
(cuda-gdb) break add_kernel

# νŠΉμ • μŠ€λ ˆλ“œμ— λŒ€ν•΄μ„œλ§Œ 쀑단 (문제 격리에 μ’‹μŒ)
(cuda-gdb) break add_kernel if thread_idx.x == 0

# λ©”λͺ¨λ¦¬ μ ‘κ·Ό μœ„λ°˜ μ‹œ 쀑단
(cuda-gdb) watch input_array[thread_idx.x]

# νŠΉμ • 데이터 μ‘°κ±΄μ—μ„œ 쀑단
(cuda-gdb) break add_kernel if input_array[thread_idx.x] > 100.0

이λ₯Ό 톡해 수천 개 μŠ€λ ˆλ“œμ˜ 좜λ ₯에 νŒŒλ¬»νžˆμ§€ μ•Šκ³  μ •ν™•νžˆ 관심 μžˆλŠ” μŠ€λ ˆλ“œμ™€ 쑰건에 집쀑할 수 μžˆμŠ΅λ‹ˆλ‹€.


ν™˜κ²½ μ€€λΉ„ν•˜κΈ°

디버깅을 μ‹œμž‘ν•˜κΈ° 전에 개발 ν™˜κ²½μ΄ μ œλŒ€λ‘œ κ΅¬μ„±λ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•˜μ„Έμš”. 이전 퍼즐듀을 μ§„ν–‰ν•΄μ™”λ‹€λ©΄ λŒ€λΆ€λΆ„ 이미 μ„€μ •λ˜μ–΄ μžˆμ„ κ²ƒμž…λ‹ˆλ‹€!

μ°Έκ³ : pixi μ—†μ΄λŠ” NVIDIA 곡식 λ¦¬μ†ŒμŠ€μ—μ„œ CUDA Toolkit을 μˆ˜λ™μœΌλ‘œ μ„€μΉ˜ν•˜κ³ , λ“œλΌμ΄λ²„ ν˜Έν™˜μ„±μ„ κ΄€λ¦¬ν•˜κ³ , ν™˜κ²½ λ³€μˆ˜λ₯Ό κ΅¬μ„±ν•˜κ³ , μ»΄ν¬λ„ŒνŠΈ κ°„ 버전 μΆ©λŒμ„ μ²˜λ¦¬ν•΄μ•Ό ν•©λ‹ˆλ‹€. pixiλŠ” λͺ¨λ“  CUDA μ˜μ‘΄μ„±, 버전, ν™˜κ²½ ꡬ성을 μžλ™μœΌλ‘œ κ΄€λ¦¬ν•˜μ—¬ 이 λ³΅μž‘μ„±μ„ μ œκ±°ν•©λ‹ˆλ‹€.

pixiκ°€ 디버깅에 μ€‘μš”ν•œ 이유

문제점: GPU 디버깅은 CUDA νˆ΄ν‚·, GPU λ“œλΌμ΄λ²„, Mojo 컴파일러, 디버거 μ»΄ν¬λ„ŒνŠΈ κ°„μ˜ μ •λ°€ν•œ 쑰율이 ν•„μš”ν•©λ‹ˆλ‹€. 버전 λΆˆμΌμΉ˜λŠ” β€œλ””λ²„κ±°λ₯Ό 찾을 수 μ—†μŒβ€ 였λ₯˜λ‘œ μ΄μ–΄μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

ν•΄κ²°μ±…: pixiλ₯Ό μ‚¬μš©ν•˜λ©΄ 이 λͺ¨λ“  μ»΄ν¬λ„ŒνŠΈκ°€ μ‘°ν™”λ‘­κ²Œ μž‘λ™ν•©λ‹ˆλ‹€. pixi run mojo debug --cuda-gdbλ₯Ό μ‹€ν–‰ν•˜λ©΄ pixiκ°€ μžλ™μœΌλ‘œ:

  • CUDA νˆ΄ν‚· 경둜 μ„€μ •
  • μ˜¬λ°”λ₯Έ GPU λ“œλΌμ΄λ²„ λ‘œλ“œ
  • Mojo 디버깅 ν”ŒλŸ¬κ·ΈμΈ ꡬ성
  • ν™˜κ²½ λ³€μˆ˜λ₯Ό μΌκ΄€λ˜κ²Œ 관리

μ„€μ • 확인

λͺ¨λ“  것이 μž‘λ™ν•˜λŠ”μ§€ 확인해 λ΄…μ‹œλ‹€:

# 1. GPU ν•˜λ“œμ›¨μ–΄ μ ‘κ·Ό κ°€λŠ₯ μ—¬λΆ€ 확인
pixi run nvidia-smi
# GPU와 λ“œλΌμ΄λ²„ 버전이 ν‘œμ‹œλ˜μ–΄μ•Ό 함

# 2. CUDA-GDB 톡합 μ„€μ • (GPU 디버깅에 ν•„μš”)
pixi run setup-cuda-gdb
# μ‹œμŠ€ν…œ CUDA-GDB λ°”μ΄λ„ˆλ¦¬λ₯Ό conda ν™˜κ²½μ— 링크

# 3. Mojo 디버거 μ‚¬μš© κ°€λŠ₯ μ—¬λΆ€ 확인
pixi run mojo debug --help
# --cuda-gdbλ₯Ό ν¬ν•¨ν•œ 디버깅 μ˜΅μ…˜μ΄ ν‘œμ‹œλ˜μ–΄μ•Ό 함

# 4. CUDA-GDB 톡합 ν…ŒμŠ€νŠΈ
pixi run cuda-gdb --version
# NVIDIA CUDA-GDB 버전 정보가 ν‘œμ‹œλ˜μ–΄μ•Ό 함

이 λͺ…λ Ήμ–΄ 쀑 ν•˜λ‚˜λΌλ„ μ‹€νŒ¨ν•˜λ©΄ pixi.toml ꡬ성을 λ‹€μ‹œ ν™•μΈν•˜κ³  CUDA νˆ΄ν‚· κΈ°λŠ₯이 ν™œμ„±ν™”λ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•˜μ„Έμš”.

μ€‘μš”: conda의 cuda-gdb νŒ¨ν‚€μ§€λŠ” 래퍼 슀크립트만 μ œκ³΅ν•˜κΈ° λ•Œλ¬Έμ— pixi run setup-cuda-gdb λͺ…령이 ν•„μš”ν•©λ‹ˆλ‹€. 이 λͺ…령은 μ‹œμŠ€ν…œ CUDA μ„€μΉ˜μ—μ„œ μ‹€μ œ CUDA-GDB λ°”μ΄λ„ˆλ¦¬λ₯Ό μžλ™ κ°μ§€ν•˜κ³  conda ν™˜κ²½μ— λ§ν¬ν•˜μ—¬ 전체 GPU 디버깅 κΈ°λŠ₯을 ν™œμ„±ν™”ν•©λ‹ˆλ‹€.

이 λͺ…령이 ν•˜λŠ” 일:

μŠ€ν¬λ¦½νŠΈλŠ” μ—¬λŸ¬ 일반적인 μœ„μΉ˜μ—μ„œ CUDAλ₯Ό μžλ™ κ°μ§€ν•©λ‹ˆλ‹€:

  • $CUDA_HOME ν™˜κ²½ λ³€μˆ˜
  • /usr/local/cuda (Ubuntu/Debian κΈ°λ³Έκ°’)
  • /opt/cuda (ArchLinux 및 기타 배포판)
  • μ‹œμŠ€ν…œ PATH (which cuda-gdb 톡해)

κ΅¬ν˜„ μ„ΈλΆ€ 사항은 scripts/setup-cuda-gdb.shλ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.

WSL μ‚¬μš©μžλ₯Ό μœ„ν•œ νŠΉλ³„ 참고사항: Part IIμ—μ„œ μ‚¬μš©ν•  두 κ°€μ§€ 디버그 도ꡬ(cuda-gdb와 compute-sanitizer)λŠ” WSLμ—μ„œ CUDA μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 디버깅을 μ§€μ›ν•˜μ§€λ§Œ, λ ˆμ§€μŠ€νŠΈλ¦¬ ν‚€ HKEY_LOCAL_MACHINE\SOFTWARE\NVIDIA Corporation\GPUDebugger\EnableInterfaceλ₯Ό μΆ”κ°€ν•˜κ³  (DWORD) 1둜 μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€. μ§€μ›λ˜λŠ” ν”Œλž«νΌκ³Ό OS별 λ™μž‘μ— λŒ€ν•œ μžμ„Έν•œ λ‚΄μš©μ€ cuda-gdb와 compute-sanitizerλ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.


μ‹€μŠ΅ νŠœν† λ¦¬μ–Ό: 첫 GPU 디버깅 μ„Έμ…˜

이둠도 μ’‹μ§€λ§Œ 직접 κ²½ν—˜ν•˜λŠ” κ²ƒλ§Œ ν•œ 게 μ—†μŠ΅λ‹ˆλ‹€. Puzzle 01 - μ—¬λŸ¬λΆ„μ΄ 잘 μ•„λŠ” κ°„λ‹¨ν•œ β€œλ°°μ—΄ 각 μš”μ†Œμ— 10 λ”ν•˜κΈ°β€ 컀널을 μ‚¬μš©ν•΄μ„œ μ‹€μ œ ν”„λ‘œκ·Έλž¨μ„ 디버깅해 λ΄…μ‹œλ‹€.

μ™œ Puzzle 01인가? λ‹€μŒ 이유둜 μ™„λ²½ν•œ 디버깅 νŠœν† λ¦¬μ–Όμž…λ‹ˆλ‹€:

  • μΆ©λΆ„νžˆ λ‹¨μˆœν•΄μ„œ 무엇이 μΌμ–΄λ‚˜μ•Ό ν•˜λŠ”μ§€ 이해할 수 있음
  • μ‹€μ œ 컀널 싀행이 μžˆλŠ” μ§„μ§œ GPU μ½”λ“œ
  • CPU μ„€μ • μ½”λ“œμ™€ GPU 컀널 μ½”λ“œ λͺ¨λ‘ 포함
  • 짧은 μ‹€ν–‰ μ‹œκ°„μœΌλ‘œ λΉ λ₯Έ 반볡 κ°€λŠ₯

이 νŠœν† λ¦¬μ–Όμ΄ λλ‚˜λ©΄ λ„€ κ°€μ§€ 디버깅 접근법 λͺ¨λ‘λ‘œ 같은 ν”„λ‘œκ·Έλž¨μ„ λ””λ²„κΉ…ν•˜κ³ , μ‹€μ œ 디버거 좜λ ₯을 보고, 맀일 μ‚¬μš©ν•  ν•„μˆ˜ 디버깅 λͺ…λ Ήμ–΄λ₯Ό 배우게 λ©λ‹ˆλ‹€.

디버깅 접근법 ν•™μŠ΅ 경둜

Puzzle 01을 예제둜 λ„€ κ°€μ§€ 디버깅 쑰합을 νƒμƒ‰ν•©λ‹ˆλ‹€. ν•™μŠ΅ 경둜: μ†ŒμŠ€ + LLDB(κ°€μž₯ 쉬움)둜 μ‹œμž‘ν•΄μ„œ CUDA-GDB(κ°€μž₯ κ°•λ ₯함)둜 μ§„ν–‰ν•©λ‹ˆλ‹€.

⚠️ GPU 디버깅 μ‹œ μ€‘μš”μ‚¬ν•­:

  • --break-on-launch ν”Œλž˜κ·ΈλŠ” CUDA-GDB μ ‘κ·Όλ²•μ—μ„œ ν•„μˆ˜
  • 미리 컴파일된 λ°”μ΄λ„ˆλ¦¬ (접근법 3 & 4)λŠ” -O0 -g둜 λΉŒλ“œλ˜μ–΄ μ΅œμ ν™”κ°€ λΉ„ν™œμ„±ν™”λ˜κ³  디버그 심볼이 ν¬ν•¨λ©λ‹ˆλ‹€ β€” κ·Έλž˜μ„œ i 같은 둜컬 λ³€μˆ˜κ°€ 검사λ₯Ό μœ„ν•΄ λ³΄μ‘΄λ©λ‹ˆλ‹€
  • μ†ŒμŠ€μ—μ„œ 디버깅 (접근법 1 & 2)은 κΈ°λ³Έ ν”Œλž˜κ·Έλ‘œ μ»΄νŒŒμΌλ˜λ―€λ‘œ, λŒ€λΆ€λΆ„μ˜ 둜컬 λ³€μˆ˜κ°€ μ΅œμ ν™”λ‘œ μ œκ±°λ©λ‹ˆλ‹€
  • 본격적인 GPU λ””λ²„κΉ…μ—λŠ” 접근법 4 (λ°”μ΄λ„ˆλ¦¬ + CUDA-GDB) μ‚¬μš©

νŠœν† λ¦¬μ–Ό Step 1: LLDB둜 CPU 디버깅

κ°€μž₯ 일반적인 디버깅 μ‹œλ‚˜λ¦¬μ˜€λ‘œ μ‹œμž‘ν•©μ‹œλ‹€: ν”„λ‘œκ·Έλž¨μ΄ ν¬λž˜μ‹œν•˜κ±°λ‚˜ μ˜ˆμƒμΉ˜ λͺ»ν•œ λ™μž‘μ„ ν•΄μ„œ main() ν•¨μˆ˜μ—μ„œ 무슨 일이 μΌμ–΄λ‚˜λŠ”μ§€ 봐야 ν•  λ•Œ.

λ―Έμ…˜: Puzzle 01의 CPU μΈ‘ μ„€μ • μ½”λ“œλ₯Ό λ””λ²„κΉ…ν•˜μ—¬ Mojoκ°€ GPU λ©”λͺ¨λ¦¬λ₯Ό μ΄ˆκΈ°ν™”ν•˜κ³  컀널을 μ‹€ν–‰ν•˜λŠ” 방법을 νŒŒμ•…ν•©λ‹ˆλ‹€.

디버거 μ‹€ν–‰

μ†ŒμŠ€μ—μ„œ 직접 LLDB 디버거λ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€:

# ν•œ λ‹¨κ³„λ‘œ p01.mojoλ₯Ό μ»΄νŒŒμΌν•˜κ³  디버깅
pixi run mojo debug solutions/p01/p01.mojo

LLDB ν”„λ‘¬ν”„νŠΈκ°€ λ³΄μž…λ‹ˆλ‹€: (lldb). 이제 디버거 μ•ˆμ—μ„œ ν”„λ‘œκ·Έλž¨ 싀행을 검사할 μ€€λΉ„κ°€ λ˜μ—ˆμŠ΅λ‹ˆλ‹€!

첫 디버깅 λͺ…λ Ήμ–΄λ“€

Puzzle 01이 싀행될 λ•Œ 무슨 일이 μΌμ–΄λ‚˜λŠ”μ§€ 좔적해 λ΄…μ‹œλ‹€. λ³΄μ—¬λ“œλ¦° λŒ€λ‘œ μ •ν™•νžˆ 이 λͺ…령어듀을 μž…λ ₯ν•˜κ³  좜λ ₯을 κ΄€μ°°ν•˜μ„Έμš”:

Step 1: main ν•¨μˆ˜μ— 브레이크포인트 μ„€μ •

(lldb) br set -n main

좜λ ₯:

Breakpoint 1: no locations (pending).
WARNING: Unable to resolve breakpoint to any actual locations.

이것은 μ˜ˆμƒλ˜λŠ” λ™μž‘μž…λ‹ˆλ‹€. mojo debug your_program.mojo둜 μ‹€ν–‰ν•˜λ©΄ 디버그 μ„Έμ…˜μ΄ μ‹œμž‘λ  λ•Œ ν”„λ‘œκ·Έλž¨μ΄ μ»΄νŒŒμΌλ˜λ―€λ‘œ, 브레이크포인트λ₯Ό μ„€μ •ν•˜λŠ” μ‹œμ μ—λŠ” λͺ¨λ“ˆμ΄ 아직 디버거에 λ‘œλ“œλ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. LLDBλŠ” 브레이크포인트λ₯Ό 보λ₯˜ μƒνƒœλ‘œ λ“±λ‘ν•˜λ©° 아직 ꡬ체적인 λͺ…λ Ήμ–΄ μ£Όμ†Œμ— 바인딩할 수 μ—†μŠ΅λ‹ˆλ‹€. 싀행이 μ‹œμž‘λ˜κ³  λͺ¨λ“ˆμ΄ λ‘œλ“œλ˜λ©΄ LLDBκ°€ μžλ™μœΌλ‘œ 브레이크포인트λ₯Ό ν•΄κ²°ν•©λ‹ˆλ‹€.

Step 2: ν”„λ‘œκ·Έλž¨ μ‹œμž‘

(lldb) run

좜λ ₯:

Process 186951 launched: '/home/ubuntu/workspace/mojo-gpu-puzzles/.pixi/envs/default/bin/mojo' (x86_64)
Process 186951 stopped and restarted: thread 1 received signal: SIGCHLD
2 locations added to breakpoint 1
Process 186951 stopped
* thread #1, name = 'mojo', stop reason = breakpoint 1.2
    frame #0: 0x00007fff5c01e841 JIT(0x7fff5c075000)`std::builtin::_startup::__mojo_main_prototype(argc=1, argv=0x00007fffffffa858) at _startup.mojo:119:5

ν”„λ‘œκ·Έλž¨μ΄ μ‹œμž‘λ˜λ©΄ 보λ₯˜ μ€‘μ΄λ˜ λΈŒλ ˆμ΄ν¬ν¬μΈνŠΈκ°€ 2개의 μœ„μΉ˜λ‘œ ν•΄κ²°λ©λ‹ˆλ‹€ β€” ν•˜λ‚˜λŠ” _startup.mojo(Mojo의 λ‚΄λΆ€ μ‹œμž‘ 래퍼)에, λ‹€λ₯Έ ν•˜λ‚˜λŠ” μ—¬λŸ¬λΆ„μ˜ p01.mojo의 main에 μœ„μΉ˜ν•©λ‹ˆλ‹€. LLDBλŠ” 첫 번째 히트인 μ‹œμž‘ λž˜νΌμ—μ„œ 멈μΆ₯λ‹ˆλ‹€. SIGCHLD μ‹œκ·Έλ„μ€ μ •μƒμž…λ‹ˆλ‹€ β€” Mojoκ°€ λ‚΄λΆ€ ν”„λ‘œμ„ΈμŠ€λ₯Ό κ΄€λ¦¬ν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€.

Step 3: μ‹€μ œ μ½”λ“œλ‘œ 계속

# p01.mojo μ½”λ“œμ— λ„λ‹¬ν•˜κΈ° μœ„ν•΄ continue
(lldb) continue

좜λ ₯:

Process 186951 resuming
Process 186951 stopped
* thread #1, name = 'mojo', stop reason = breakpoint 1.1
    frame #0: 0x00007fff5c014040 JIT(0x7fff5c075000)`p01::main() at p01.mojo:30:23
   27
   28
   29   def main() raises:
-> 30       with DeviceContext() as ctx:
   31           var out = ctx.enqueue_create_buffer[dtype](SIZE)
   32           out.enqueue_fill(0)
   33           var a = ctx.enqueue_create_buffer[dtype](SIZE)

이제 μ‹€μ œ Mojo μ†ŒμŠ€ μ½”λ“œλ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. μ£Όλͺ©ν•  점:

  • p01.mojo 파일의 27-33번 쀄
  • ν˜„μž¬ 쀄 30: with DeviceContext() as ctx:
  • μΈν”„λ‘œμ„ΈμŠ€ μ‹€ν–‰: JIT(0x7fff5c075000) μ ‘λ‘μ‚¬λŠ” μ‹€ν–‰ 쀑인 ν”„λ‘œμ„ΈμŠ€μ— λ‘œλ“œλœ μ½”λ“œ μ˜μ—­μ— λŒ€ν•œ LLDB의 λΌλ²¨μž…λ‹ˆλ‹€. mojo debug your_program.mojo둜 μ‹€ν–‰ν•˜λ©΄, 컴파일된 ν”„λ‘œκ·Έλž¨μ΄ λ””μŠ€ν¬μ— μ‹€ν–‰ νŒŒμΌμ„ μ“°μ§€ μ•Šκ³  μΈν”„λ‘œμ„ΈμŠ€μ—μ„œ μ‹€ν–‰λ©λ‹ˆλ‹€ β€” κ·Έλž˜μ„œ 이 κ²½λ‘œκ°€ λ¨Όμ € λ°”μ΄λ„ˆλ¦¬λ₯Ό λΉŒλ“œν•˜λŠ” 것보닀 μ‹œμž‘μ΄ λΉ λ₯΄κ³ , your_program_debug 파일이 μƒμ„±λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€

Step 4: ν”„λ‘œκ·Έλž¨ μ™„λ£Œ

# ν”„λ‘œκ·Έλž¨μ„ μ™„λ£ŒκΉŒμ§€ μ‹€ν–‰
(lldb) continue

좜λ ₯:

Process 186951 resuming
out: HostBuffer([10.0, 11.0, 12.0, 13.0])
expected: HostBuffer([10.0, 11.0, 12.0, 13.0])
Process 186951 exited with status = 0 (0x00000000)

배운 λ‚΄μš©

πŸŽ“ μΆ•ν•˜ν•©λ‹ˆλ‹€! 첫 GPU ν”„λ‘œκ·Έλž¨ 디버깅 μ„Έμ…˜μ„ μ™„λ£Œν–ˆμŠ΅λ‹ˆλ‹€. 무슨 일이 μžˆμ—ˆλŠ”μ§€ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€:

거쳐온 디버깅 μ—¬μ •:

  1. Mojo μ‹œμž‘ κ³Όμ • 탐색 - Mojo에 λ‚΄λΆ€ μ΄ˆκΈ°ν™” μ½”λ“œκ°€ μžˆμŒμ„ ν•™μŠ΅ (브레이크 ν¬μΈνŠΈκ°€ 두 μœ„μΉ˜λ‘œ ν•΄κ²°λ˜μ–΄ λ¨Όμ € _startup.mojoμ—μ„œ λ©ˆμ·„μŠ΅λ‹ˆλ‹€)
  2. μ†ŒμŠ€ μ½”λ“œ 도달 - ꡬ문 κ°•μ‘°κ°€ 된 μ‹€μ œ p01.mojo 27-33번 쀄 확인
  3. μΈν”„λ‘œμ„ΈμŠ€ μ‹€ν–‰ κ΄€μ°° - mojo debug your_program.mojoκ°€ ν”„λ‘œκ·Έλž¨μ„ μ»΄νŒŒμΌν•˜κ³  λ””μŠ€ν¬μ— μ‹€ν–‰ νŒŒμΌμ„ μ“°μ§€ μ•Šκ³  λ°”λ‘œ μ‹€ν–‰ν•˜λŠ” 것을 κ΄€μ°°
  4. 성곡적인 μ‹€ν–‰ 확인 - ν”„λ‘œκ·Έλž¨μ΄ μ˜ˆμƒλœ 좜λ ₯을 생성함을 확인

LLDB 디버깅이 μ œκ³΅ν•˜λŠ” 것:

  • βœ… CPU μΈ‘ κ°€μ‹œμ„±: main() ν•¨μˆ˜, 버퍼 ν• λ‹Ή, λ©”λͺ¨λ¦¬ μ„€μ • 확인
  • βœ… μ†ŒμŠ€ μ½”λ“œ 검사: 쀄 λ²ˆν˜Έκ°€ μžˆλŠ” μ‹€μ œ Mojo μ½”λ“œ 보기
  • βœ… λ³€μˆ˜ 검사: 호슀트 μΈ‘ λ³€μˆ˜(CPU λ©”λͺ¨λ¦¬) κ°’ 확인
  • βœ… ν”„λ‘œκ·Έλž¨ 흐름 μ œμ–΄: μ„€μ • λ‘œμ§μ„ 쀄 λ‹¨μœ„λ‘œ 단계별 μ‹€ν–‰
  • βœ… 였λ₯˜ 쑰사: μž₯치 μ„€μ •, λ©”λͺ¨λ¦¬ ν• λ‹Ή λ“±μ˜ ν¬λž˜μ‹œ 디버깅

LLDBκ°€ ν•  수 μ—†λŠ” 것:

  • ❌ GPU 컀널 검사: add_10 ν•¨μˆ˜ μ‹€ν–‰ λ‚΄λΆ€λ‘œ μ§„μž… λΆˆκ°€λŠ₯
  • ❌ μŠ€λ ˆλ“œ μˆ˜μ€€ 디버깅: κ°œλ³„ GPU μŠ€λ ˆλ“œ λ™μž‘ 확인 λΆˆκ°€
  • ❌ GPU λ©”λͺ¨λ¦¬ μ ‘κ·Ό: GPU μŠ€λ ˆλ“œκ°€ λ³΄λŠ” 데이터 검사 λΆˆκ°€
  • ❌ 병렬 μ‹€ν–‰ 뢄석: 경쟁 μƒνƒœλ‚˜ 동기화 디버깅 λΆˆκ°€

LLDB 디버깅을 μ‚¬μš©ν•  λ•Œ:

  • GPU μ½”λ“œκ°€ μ‹€ν–‰λ˜κΈ° 전에 ν”„λ‘œκ·Έλž¨μ΄ ν¬λž˜μ‹œν•  λ•Œ
  • 버퍼 ν• λ‹Ήμ΄λ‚˜ λ©”λͺ¨λ¦¬ μ„€μ • 문제
  • ν”„λ‘œκ·Έλž¨ μ΄ˆκΈ°ν™”μ™€ 흐름 이해
  • Mojo μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ–΄λ–»κ²Œ μ‹œμž‘λ˜λŠ”μ§€ ν•™μŠ΅
  • λΉ λ₯Έ ν”„λ‘œν† νƒ€μ΄ν•‘κ³Ό μ½”λ“œ λ³€κ²½ μ‹€ν—˜

핡심 톡찰: LLDBλŠ” 호슀트 μΈ‘ 디버깅에 μ™„λ²½ν•©λ‹ˆλ‹€ - GPU μ‹€ν–‰ 전후에 CPUμ—μ„œ μΌμ–΄λ‚˜λŠ” λͺ¨λ“  것. μ‹€μ œ GPU 컀널 λ””λ²„κΉ…μ—λŠ” λ‹€μŒ 접근법이 ν•„μš”ν•©λ‹ˆλ‹€β€¦

νŠœν† λ¦¬μ–Ό Step 2: λ°”μ΄λ„ˆλ¦¬ 디버깅

μ†ŒμŠ€ 디버깅을 λ°°μ› μœΌλ‹ˆ 이제 ν”„λ‘œλ•μ…˜ ν™˜κ²½μ—μ„œ μ‚¬μš©ν•˜λŠ” 전문적인 접근법을 νƒμƒ‰ν•©μ‹œλ‹€.

μ‹œλ‚˜λ¦¬μ˜€: μ—¬λŸ¬ 파일이 μžˆλŠ” λ³΅μž‘ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ””λ²„κΉ…ν•˜κ±°λ‚˜ 같은 ν”„λ‘œκ·Έλž¨μ„ 반볡적으둜 디버깅해야 ν•©λ‹ˆλ‹€. λ¨Όμ € λ°”μ΄λ„ˆλ¦¬λ₯Ό λΉŒλ“œν•˜λ©΄ 더 λ§Žμ€ μ œμ–΄μ™€ λΉ λ₯Έ 디버깅 반볡이 κ°€λŠ₯ν•©λ‹ˆλ‹€.

디버그 λ°”μ΄λ„ˆλ¦¬ λΉŒλ“œ

Step 1: 디버그 μ •λ³΄λ‘œ 컴파일

# 디버그 λΉŒλ“œ 생성 (λͺ…ν™•ν•œ λͺ…λͺ…에 μ£Όλͺ©)
pixi run mojo build -O0 -g solutions/p01/p01.mojo -o solutions/p01/p01_debug

μ—¬κΈ°μ„œ μΌμ–΄λ‚˜λŠ” 일:

  • πŸ”§ -O0: μ΅œμ ν™” λΉ„ν™œμ„±ν™” (μ •ν™•ν•œ 디버깅에 λ°˜λ“œμ‹œ ν•„μš”)
  • πŸ” -g: λ¨Έμ‹  μ½”λ“œλ₯Ό μ†ŒμŠ€ μ½”λ“œμ— λ§€ν•‘ν•˜λŠ” 디버그 심볼 포함
  • πŸ“ -o p01_debug: λͺ…ν™•ν•˜κ²Œ 이름 지은 디버그 λ°”μ΄λ„ˆλ¦¬ 생성

Step 2: λ°”μ΄λ„ˆλ¦¬ 디버깅

# 미리 λΉŒλ“œλœ λ°”μ΄λ„ˆλ¦¬ 디버깅
pixi run mojo debug solutions/p01/p01_debug

무엇이 λ‹€λ₯Έκ°€ (그리고 더 λ‚˜μ€κ°€)

μ‹œμž‘ 비ꡐ:

μ†ŒμŠ€ λ””λ²„κΉ…λ°”μ΄λ„ˆλ¦¬ 디버깅
ν•œ λ‹¨κ³„λ‘œ 컴파일 + λ””λ²„κΉ…ν•œ 번 λΉŒλ“œ, μ—¬λŸ¬ 번 디버깅
느린 μ‹œμž‘ (컴파일 μ˜€λ²„ν—€λ“œ)λΉ λ₯Έ μ‹œμž‘
컴파일 λ©”μ‹œμ§€κ°€ 디버그 좜λ ₯κ³Ό μ„žμž„κΉ”λ”ν•œ 디버거 좜λ ₯
κΈ°λ³Έ λΉŒλ“œ ν”Œλž˜κ·Έλͺ…μ‹œμ  -O0 -g λΉŒλ“œ ν”Œλž˜κ·Έ

같은 LLDB λͺ…λ Ήμ–΄(br set -n main, run, continue)λ₯Ό μ‹€ν–‰ν•˜λ©΄ λ‹€μŒκ³Ό 같은 차이λ₯Ό λŠλ‚„ 수 μžˆμŠ΅λ‹ˆλ‹€:

  • λΉ λ₯Έ μ‹œμž‘ - 컴파일 μ§€μ—° μ—†μŒ
  • κΉ”λ”ν•œ 좜λ ₯ - 컴파일 λ©”μ‹œμ§€κ°€ μ„žμ΄μ§€ μ•ŠμŒ
  • 더 예츑 κ°€λŠ₯ - λΉŒλ“œ ν”Œλž˜κ·Έκ°€ λͺ…μ‹œμ μ΄κ³  μ‹€ν–‰ 간에 μž¬μ‚¬μš©λ¨
  • 전문적인 μ›Œν¬ν”Œλ‘œμš° - ν”„λ‘œλ•μ…˜ 디버깅이 μ΄λ ‡κ²Œ μž‘λ™ν•¨

νŠœν† λ¦¬μ–Ό Step 3: GPU 컀널 디버깅

μ§€κΈˆκΉŒμ§€λŠ” CPU 호슀트 μ½”λ“œ - μ„€μ •, λ©”λͺ¨λ¦¬ ν• λ‹Ή, μ΄ˆκΈ°ν™”λ₯Ό λ””λ²„κΉ…ν–ˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ 병렬 연산이 μΌμ–΄λ‚˜λŠ” μ‹€μ œ GPU 컀널은 μ–΄λ–¨κΉŒμš”?

문제점: add_10 컀널은 잠재적으둜 수천 개의 μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— μ‹€ν–‰λ˜λŠ” GPUμ—μ„œ μ‹€ν–‰λ©λ‹ˆλ‹€. LLDBλŠ” GPU의 병렬 μ‹€ν–‰ ν™˜κ²½μ— μ ‘κ·Όν•  수 μ—†μŠ΅λ‹ˆλ‹€.

ν•΄κ²°μ±…: CUDA-GDB - GPU μŠ€λ ˆλ“œ, GPU λ©”λͺ¨λ¦¬, 병렬 싀행을 μ΄ν•΄ν•˜λŠ” μ „λ¬Έ λ””λ²„κ±°μž…λ‹ˆλ‹€.

CUDA-GDBκ°€ ν•„μš”ν•œ 이유

GPU 디버깅이 근본적으둜 λ‹€λ₯Έ 이유λ₯Ό μ΄ν•΄ν•©μ‹œλ‹€:

CPU 디버깅 (LLDB):

  • 순차적으둜 μ‹€ν–‰λ˜λŠ” 단일 μŠ€λ ˆλ“œ
  • 좔적할 콜 μŠ€νƒμ΄ ν•˜λ‚˜λΏ
  • λ‹¨μˆœν•œ λ©”λͺ¨λ¦¬ λͺ¨λΈ
  • λ³€μˆ˜κ°€ 단일 값을 가짐

GPU 디버깅 (CUDA-GDB):

  • λ³‘λ ¬λ‘œ μ‹€ν–‰λ˜λŠ” 수천 개의 μŠ€λ ˆλ“œ
  • μ—¬λŸ¬ 콜 μŠ€νƒ (μŠ€λ ˆλ“œλ‹Ή ν•˜λ‚˜)
  • λ³΅μž‘ν•œ λ©”λͺ¨λ¦¬ 계측 ꡬ쑰 (μ „μ—­, 곡유, 둜컬, λ ˆμ§€μŠ€ν„°)
  • 같은 λ³€μˆ˜κ°€ μŠ€λ ˆλ“œλ§ˆλ‹€ λ‹€λ₯Έ 값을 가짐

μ‹€μ œ 예: add_10 μ»€λ„μ—μ„œ thread_idx.x λ³€μˆ˜λŠ” 각 μŠ€λ ˆλ“œλ§ˆλ‹€ λ‹€λ₯Έ 값을 κ°€μ§‘λ‹ˆλ‹€ - μŠ€λ ˆλ“œ 0은 0을, μŠ€λ ˆλ“œ 1은 1을 λ³΄λŠ” μ‹μž…λ‹ˆλ‹€. CUDA-GDB만이 이 병렬 ν˜„μ‹€μ„ 보여쀄 수 μžˆμŠ΅λ‹ˆλ‹€.

CUDA-GDB 디버거 μ‹€ν–‰

Step 1: GPU 컀널 디버깅 μ‹œμž‘

접근법을 μ„ νƒν•˜μ„Έμš”:

# 이미 μ‹€ν–‰ν–ˆλŠ”μ§€ 확인 (ν•œ 번이면 μΆ©λΆ„)
pixi run setup-cuda-gdb

# μ†ŒμŠ€ + CUDA-GDB μ‚¬μš© (μœ„μ˜ 접근법 2)
pixi run mojo debug --cuda-gdb --break-on-launch solutions/p01/p01.mojo

ν•™μŠ΅κ³Ό λΉ λ₯Έ λ°˜λ³΅μ— μ ν•©ν•œ μ†ŒμŠ€ + CUDA-GDB 접근법을 μ‚¬μš©ν•©λ‹ˆλ‹€.

Step 2: μ‹€ν–‰ν•˜κ³  GPU 컀널 μ§„μž… μ‹œ μžλ™ μ •μ§€

CUDA-GDB ν”„λ‘¬ν”„νŠΈλŠ” μ΄λ ‡κ²Œ λ³΄μž…λ‹ˆλ‹€: (cuda-gdb). ν”„λ‘œκ·Έλž¨μ„ μ‹œμž‘ν•©λ‹ˆλ‹€:

# ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ - GPU 컀널이 싀행될 λ•Œ μžλ™μœΌλ‘œ μ •μ§€
(cuda-gdb) run

좜λ ₯:

Starting program: /home/ubuntu/workspace/mojo-gpu-puzzles/.pixi/envs/default/bin/mojo...
[Thread debugging using libthread_db enabled]
...
[Switching focus to CUDA kernel 0, grid 1, block (0,0,0), thread (0,0,0)]

CUDA thread hit application kernel entry function breakpoint, p01_add_10_UnsafePointer...
   <<<(1,1,1),(4,1,1)>>> (output=0x302000000, a=0x302000200) at p01.mojo:16
16          i = thread_idx.x

성곡! GPU 컀널 λ‚΄λΆ€μ—μ„œ μžλ™μœΌλ‘œ μ •μ§€ν–ˆμŠ΅λ‹ˆλ‹€! --break-on-launch ν”Œλž˜κ·Έκ°€ 컀널 싀행을 κ°μ§€ν–ˆκ³  이제 i = thread_idx.xκ°€ μ‹€ν–‰λ˜λŠ” 16번 쀄에 μžˆμŠ΅λ‹ˆλ‹€.

μ€‘μš”: break add_10처럼 μˆ˜λ™μœΌλ‘œ 브레이크포인트λ₯Ό μ„€μ •ν•  ν•„μš” μ—†μŠ΅λ‹ˆλ‹€

  • 컀널 μ§„μž… λΈŒλ ˆμ΄ν¬ν¬μΈνŠΈλŠ” μžλ™μž…λ‹ˆλ‹€. GPU 컀널 ν•¨μˆ˜λŠ” CUDA-GDBμ—μ„œ λ§ΉκΈ€λ§λœ 이름(p01_add_10_UnsafePointer... 같은)을 κ°€μ§€μ§€λ§Œ, 이미 컀널 μ•ˆμ— μžˆμœΌλ―€λ‘œ λ°”λ‘œ 디버깅을 μ‹œμž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Step 3: 병렬 μ‹€ν–‰ 탐색

# λΈŒλ ˆμ΄ν¬ν¬μΈνŠΈμ—μ„œ μΌμ‹œ μ •μ§€λœ λͺ¨λ“  GPU μŠ€λ ˆλ“œ 보기
(cuda-gdb) info cuda threads

좜λ ₯:

  BlockIdx ThreadIdx To BlockIdx To ThreadIdx Count                 PC                                                       Filename  Line
Kernel 0
*  (0,0,0)   (0,0,0)     (0,0,0)      (3,0,0)     4 0x00007fffd326fb70 /home/ubuntu/workspace/mojo-gpu-puzzles/solutions/p01/p01.mojo    16

μ™„λ²½ν•©λ‹ˆλ‹€! Puzzle 01의 λͺ¨λ“  4개 병렬 GPU μŠ€λ ˆλ“œλ₯Ό λ³΄μ—¬μ€λ‹ˆλ‹€:

  • *κ°€ ν˜„μž¬ μŠ€λ ˆλ“œ ν‘œμ‹œ: (0,0,0) - 디버깅 쀑인 μŠ€λ ˆλ“œ
  • μŠ€λ ˆλ“œ λ²”μœ„: (0,0,0)μ—μ„œ (3,0,0)κΉŒμ§€ - λΈ”λ‘μ˜ λͺ¨λ“  4개 μŠ€λ ˆλ“œ
  • Count: 4 - μ½”λ“œμ˜ THREADS_PER_BLOCK = 4와 일치
  • 같은 μœ„μΉ˜: λͺ¨λ“  μŠ€λ ˆλ“œκ°€ p01.mojo의 16번 μ€„μ—μ„œ μΌμ‹œ μ •μ§€

Step 4: 컀널을 단계별 μ‹€ν–‰ν•˜κ³  λ³€μˆ˜ 검사

# 'next'둜 μ½”λ“œ 단계별 μ‹€ν–‰ ('step'은 λ‚΄λΆ€λ‘œ 듀어감)
(cuda-gdb) next

좜λ ₯:

p01_add_10_UnsafePointer... at p01.mojo:17
17          output[i] = a[i] + 10.0
# 둜컬 λ³€μˆ˜λŠ” 미리 컴파일된 λ°”μ΄λ„ˆλ¦¬μ—μ„œ μž‘λ™!
(cuda-gdb) print i

좜λ ₯:

$1 = 0                    # 이 μŠ€λ ˆλ“œμ˜ 인덱슀 (thread_idx.x κ°’ 캑처)
# GPU λ‚΄μž₯ λ³€μˆ˜λŠ” μž‘λ™ν•˜μ§€ μ•Šμ§€λ§Œ ν•„μš” μ—†μŒ
(cuda-gdb) print thread_idx.x

좜λ ₯:

No symbol "thread_idx" in current context.
# 둜컬 λ³€μˆ˜λ₯Ό μ‚¬μš©ν•΄ μŠ€λ ˆλ“œλ³„ 데이터 μ ‘κ·Ό
(cuda-gdb) print a[i]     # 이 μŠ€λ ˆλ“œμ˜ μž…λ ₯: a[0]

좜λ ₯:

$2 = {0}                  # μž…λ ₯ κ°’ (Mojo 슀칼라 ν˜•μ‹)
(cuda-gdb) print output[i] # μ—°μ‚° μ „ 이 μŠ€λ ˆλ“œμ˜ 좜λ ₯

좜λ ₯:

$3 = {0}                  # 아직 0 - 연산이 아직 μ‹€ν–‰λ˜μ§€ μ•ŠμŒ!
# μ—°μ‚° 쀄 μ‹€ν–‰
(cuda-gdb) next

좜λ ₯:

13      fn add_10(         # μ—°μ‚° ν›„ ν•¨μˆ˜ μ‹œκ·Έλ‹ˆμ²˜ μ€„λ‘œ 이동
# 이제 κ²°κ³Ό 확인
(cuda-gdb) print output[i]

좜λ ₯:

$4 = {10}                 # 이제 κ³„μ‚°λœ κ²°κ³Ό ν‘œμ‹œ: 0 + 10 = 10
# ν•¨μˆ˜ νŒŒλΌλ―Έν„°λŠ” μ—¬μ „νžˆ μ‚¬μš© κ°€λŠ₯
(cuda-gdb) print a

좜λ ₯:

$5 = (!kgen.scalar<f32> * @register) 0x302000200

Step 5: 병렬 μŠ€λ ˆλ“œ κ°„ 이동

# λ‹€λ₯Έ μŠ€λ ˆλ“œλ‘œ μ „ν™˜ν•΄μ„œ μ‹€ν–‰ 확인
(cuda-gdb) cuda thread (1,0,0)

좜λ ₯:

[Switching focus to CUDA kernel 0, grid 1, block (0,0,0), thread (1,0,0), device 0, sm 0, warp 0, lane 1]
13      fn add_10(         # μŠ€λ ˆλ“œ 1도 ν•¨μˆ˜ μ‹œκ·Έλ‹ˆμ²˜μ— 있음
# μŠ€λ ˆλ“œμ˜ 둜컬 λ³€μˆ˜ 확인
(cuda-gdb) print i

좜λ ₯:

$5 = 1                    # μŠ€λ ˆλ“œ 1의 인덱슀 (μŠ€λ ˆλ“œ 0κ³Ό 닀름!)
# 이 μŠ€λ ˆλ“œκ°€ μ²˜λ¦¬ν•˜λŠ” 것 검사
(cuda-gdb) print a[i]     # 이 μŠ€λ ˆλ“œμ˜ μž…λ ₯: a[1]

좜λ ₯:

$6 = {1}                  # μŠ€λ ˆλ“œ 1의 μž…λ ₯ κ°’
# μŠ€λ ˆλ“œ 1의 연산은 이미 μ™„λ£Œ (병렬 μ‹€ν–‰!)
(cuda-gdb) print output[i] # 이 μŠ€λ ˆλ“œμ˜ 좜λ ₯: output[1]

좜λ ₯:

$7 = {11}                 # 1 + 10 = 11 (이미 계산됨)
# 졜고의 기법: λͺ¨λ“  μŠ€λ ˆλ“œ κ²°κ³Όλ₯Ό ν•œ λ²ˆμ— 보기
(cuda-gdb) print output[0]@4

좜λ ₯:

$8 = {{10}, {11}, {12}, {13}}     # λͺ¨λ“  4개 μŠ€λ ˆλ“œμ˜ κ²°κ³Όλ₯Ό ν•œ λͺ…λ Ήμ–΄λ‘œ!
(cuda-gdb) print a[0]@4

좜λ ₯:

$9 = {{0}, {1}, {2}, {3}}         # 비ꡐλ₯Ό μœ„ν•œ λͺ¨λ“  μž…λ ₯ κ°’
# λ„ˆλ¬΄ 멀리 μ§„ν–‰ν•˜λ©΄ CUDA μ»¨ν…μŠ€νŠΈλ₯Ό μžƒμŠ΅λ‹ˆλ‹€
(cuda-gdb) next

좜λ ₯:

[Switching to Thread 0x7ffff7e25840 (LWP 306942)]  # 호슀트 μŠ€λ ˆλ“œλ‘œ 볡귀
0x00007fffeca3f831 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
(cuda-gdb) print output[i]

좜λ ₯:

No symbol "output" in current context.  # GPU μ»¨ν…μŠ€νŠΈλ₯Ό μžƒμŒ!

이 디버깅 μ„Έμ…˜μ˜ 핡심 톡찰:

  • 🀯 병렬 싀행은 μ§„μ§œμž…λ‹ˆλ‹€ - μŠ€λ ˆλ“œ (1,0,0)으둜 μ „ν™˜ν•˜λ©΄ 이미 연산이 μ™„λ£Œλ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€!
  • 각 μŠ€λ ˆλ“œλŠ” μ„œλ‘œ λ‹€λ₯Έ 데이터λ₯Ό λ΄…λ‹ˆλ‹€ - i=0 vs i=1, a[i]={0} vs a[i]={1}, output[i]={10} vs output[i]={11}
  • λ°°μ—΄ 검사가 κ°•λ ₯ν•©λ‹ˆλ‹€ - print output[0]@4둜 λͺ¨λ“  μŠ€λ ˆλ“œμ˜ κ²°κ³Όλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€: {{10}, {11}, {12}, {13}}
  • GPU μ»¨ν…μŠ€νŠΈλŠ” κΉ¨μ§€κΈ° μ‰½μŠ΅λ‹ˆλ‹€ - λ„ˆλ¬΄ 멀리 μ§„ν–‰ν•˜λ©΄ 호슀트 μŠ€λ ˆλ“œλ‘œ λŒμ•„κ°€ GPU λ³€μˆ˜μ— μ ‘κ·Όν•  수 μ—†κ²Œ λ©λ‹ˆλ‹€

이것이 λ°”λ‘œ 병렬 μ»΄ν“¨νŒ…μ˜ λ³Έμ§ˆμž…λ‹ˆλ‹€: 같은 μ½”λ“œ, μŠ€λ ˆλ“œλ§ˆλ‹€ λ‹€λ₯Έ 데이터, λ™μ‹œ μ‹€ν–‰.

CUDA-GDB둜 배운 λ‚΄μš©

미리 컴파일된 λ°”μ΄λ„ˆλ¦¬λ‘œ GPU 컀널 μ‹€ν–‰ 디버깅을 μ™„λ£Œν–ˆμŠ΅λ‹ˆλ‹€. λ‹€μŒμ€ μ‹€μ œλ‘œ μž‘λ™ν•˜λŠ” κΈ°λŠ₯λ“€μž…λ‹ˆλ‹€:

μŠ΅λ“ν•œ GPU 디버깅 λŠ₯λ ₯:

  • βœ… GPU 컀널 μžλ™ 디버깅 - --break-on-launchκ°€ 컀널 μ§„μž… μ‹œμ μ—μ„œ μ •μ§€ν•©λ‹ˆλ‹€
  • βœ… GPU μŠ€λ ˆλ“œ κ°„ 이동 - cuda thread둜 μ»¨ν…μŠ€νŠΈλ₯Ό μ „ν™˜ν•©λ‹ˆλ‹€
  • βœ… 둜컬 λ³€μˆ˜ μ ‘κ·Ό - -O0 -g둜 컴파일된 λ°”μ΄λ„ˆλ¦¬μ—μ„œ print iκ°€ μž‘λ™ν•©λ‹ˆλ‹€
  • βœ… μŠ€λ ˆλ“œλ³„ 데이터 검사 - 각 μŠ€λ ˆλ“œκ°€ μ„œλ‘œ λ‹€λ₯Έ i, a[i], output[i] 값을 λ³΄μ—¬μ€λ‹ˆλ‹€
  • βœ… λͺ¨λ“  μŠ€λ ˆλ“œ κ²°κ³Ό 보기 - print output[0]@4둜 {{10}, {11}, {12}, {13}}을 ν•œ λ²ˆμ— ν‘œμ‹œν•©λ‹ˆλ‹€
  • βœ… GPU μ½”λ“œ 단계별 μ‹€ν–‰ - nextκ°€ 연산을 μ‹€ν–‰ν•˜κ³  κ²°κ³Όλ₯Ό λ³΄μ—¬μ€λ‹ˆλ‹€
  • βœ… 병렬 μ‹€ν–‰ 확인 - μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— μ‹€ν–‰λ©λ‹ˆλ‹€ (μ „ν™˜ν•˜λ©΄ λ‹€λ₯Έ μŠ€λ ˆλ“œλŠ” 이미 계산 μ™„λ£Œ)
  • βœ… ν•¨μˆ˜ νŒŒλΌλ―Έν„° μ ‘κ·Ό - outputκ³Ό a 포인터λ₯Ό 검사할 수 μžˆμŠ΅λ‹ˆλ‹€
  • ❌ GPU λ‚΄μž₯ λ³€μˆ˜ μ‚¬μš© λΆˆκ°€ - thread_idx.x, blockIdx.x 등은 μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€ (ν•˜μ§€λ§Œ 둜컬 λ³€μˆ˜λŠ” μž‘λ™ν•©λ‹ˆλ‹€!)
  • πŸ“Š Mojo 슀칼라 ν˜•μ‹ - 값이 10.0 λŒ€μ‹  {10}으둜 ν‘œμ‹œλ©λ‹ˆλ‹€
  • ⚠️ κΉ¨μ§€κΈ° μ‰¬μš΄ GPU μ»¨ν…μŠ€νŠΈ - λ„ˆλ¬΄ 멀리 μ§„ν–‰ν•˜λ©΄ GPU λ³€μˆ˜μ— μ ‘κ·Όν•  수 μ—†κ²Œ λ©λ‹ˆλ‹€

핡심 톡찰:

  • 미리 컴파일된 λ°”μ΄λ„ˆλ¦¬ (mojo build -O0 -g)λŠ” ν•„μˆ˜μž…λ‹ˆλ‹€ - 둜컬 λ³€μˆ˜κ°€ λ³΄μ‘΄λ©λ‹ˆλ‹€
  • @N을 μ‚¬μš©ν•œ λ°°μ—΄ 검사 - λͺ¨λ“  병렬 κ²°κ³Όλ₯Ό ν•œ λ²ˆμ— λ³΄λŠ” κ°€μž₯ 효율적인 λ°©λ²•μž…λ‹ˆλ‹€
  • GPU λ‚΄μž₯ λ³€μˆ˜λŠ” μ—†μŠ΅λ‹ˆλ‹€ - ν•˜μ§€λ§Œ i 같은 둜컬 λ³€μˆ˜κ°€ ν•„μš”ν•œ 정보λ₯Ό λ‹΄κ³  μžˆμŠ΅λ‹ˆλ‹€
  • MojoλŠ” {value} ν˜•μ‹μ„ μ‚¬μš©ν•©λ‹ˆλ‹€ - μŠ€μΉΌλΌκ°€ 10.0 λŒ€μ‹  {10}으둜 ν‘œμ‹œλ©λ‹ˆλ‹€
  • 단계별 싀행에 μ£Όμ˜ν•˜μ„Έμš” - GPU μ»¨ν…μŠ€νŠΈλ₯Ό μžƒκ³  호슀트 μŠ€λ ˆλ“œλ‘œ λŒμ•„κ°€κΈ° μ‰½μŠ΅λ‹ˆλ‹€

μ‹€μ œ 디버깅 기법듀

이제 μ‹€μ œ GPU ν”„λ‘œκ·Έλž˜λ°μ—μ„œ 마주치게 될 μ‹€μš©μ μΈ 디버깅 μ‹œλ‚˜λ¦¬μ˜€λ₯Ό μ‚΄νŽ΄λ΄…μ‹œλ‹€:

기법 1: μŠ€λ ˆλ“œ 경계 확인

# λͺ¨λ“  4개 μŠ€λ ˆλ“œκ°€ μ˜¬λ°”λ₯΄κ²Œ κ³„μ‚°ν–ˆλŠ”μ§€ 확인
(cuda-gdb) print output[0]@4

좜λ ₯:

$8 = {{10}, {11}, {12}, {13}}    # λͺ¨λ“  4개 μŠ€λ ˆλ“œκ°€ μ˜¬λ°”λ₯΄κ²Œ 계산
# 유효 λ²”μœ„λ₯Ό λ„˜μ–΄ ν™•μΈν•˜μ—¬ λ²”μœ„ 초과 문제 감지
(cuda-gdb) print output[0]@5

좜λ ₯:

$9 = {{10}, {11}, {12}, {13}, {0}}  # μš”μ†Œ 4λŠ” μ΄ˆκΈ°ν™”λ˜μ§€ μ•ŠμŒ (μ’‹μŒ!)
# μž…λ ₯κ³Ό λΉ„κ΅ν•˜μ—¬ μ—°μ‚° 검증
(cuda-gdb) print a[0]@4

좜λ ₯:

$10 = {{0}, {1}, {2}, {3}}       # μž…λ ₯ κ°’: 0+10=10, 1+10=11 λ“±

이것이 μ€‘μš”ν•œ 이유: λ²”μœ„ 초과 접근은 GPU ν¬λž˜μ‹œμ˜ κ°€μž₯ ν”ν•œ μ›μΈμž…λ‹ˆλ‹€. 이런 디버깅 λ‹¨κ³„λ‘œ 일찍 λ°œκ²¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

기법 2: μŠ€λ ˆλ“œ ꡬ성 이해

# μŠ€λ ˆλ“œκ°€ λΈ”λ‘μœΌλ‘œ μ–΄λ–»κ²Œ κ΅¬μ„±λ˜λŠ”μ§€ 보기
(cuda-gdb) info cuda blocks

좜λ ₯:

  BlockIdx To BlockIdx Count   State
Kernel 0
*  (0,0,0)     (0,0,0)     1 running
# ν˜„μž¬ λΈ”λ‘μ˜ λͺ¨λ“  μŠ€λ ˆλ“œ 보기
(cuda-gdb) info cuda threads

좜λ ₯은 μ–΄λ–€ μŠ€λ ˆλ“œκ°€ ν™œμ„± μƒνƒœμΈμ§€, μ •μ§€λ˜μ—ˆλŠ”μ§€, 였λ₯˜κ°€ μžˆλŠ”μ§€ λ³΄μ—¬μ€λ‹ˆλ‹€.

이것이 μ€‘μš”ν•œ 이유: μŠ€λ ˆλ“œ 블둝 ꡬ성을 μ΄ν•΄ν•˜λ©΄ 동기화와 곡유 λ©”λͺ¨λ¦¬ 문제λ₯Ό λ””λ²„κΉ…ν•˜λŠ” 데 도움이 λ©λ‹ˆλ‹€.

기법 3: λ©”λͺ¨λ¦¬ μ ‘κ·Ό νŒ¨ν„΄ 뢄석

# GPU λ©”λͺ¨λ¦¬ μ£Όμ†Œ 확인:
(cuda-gdb) print a               # μž…λ ₯ λ°°μ—΄ GPU 포인터

좜λ ₯:

$9 = (!kgen.scalar<f32> * @register) 0x302000200
(cuda-gdb) print output          # 좜λ ₯ λ°°μ—΄ GPU 포인터

좜λ ₯:

$10 = (!kgen.scalar<f32> * @register) 0x302000000
# 둜컬 λ³€μˆ˜λ₯Ό μ‚¬μš©ν•΄ λ©”λͺ¨λ¦¬ μ ‘κ·Ό νŒ¨ν„΄ 확인:
(cuda-gdb) print a[i]            # 각 μŠ€λ ˆλ“œκ°€ 'i'λ₯Ό μ‚¬μš©ν•΄ μžμ‹ μ˜ μš”μ†Œμ— μ ‘κ·Ό

좜λ ₯:

$11 = {0}                        # μŠ€λ ˆλ“œμ˜ μž…λ ₯ 데이터

이것이 μ€‘μš”ν•œ 이유: λ©”λͺ¨λ¦¬ μ ‘κ·Ό νŒ¨ν„΄μ€ μ„±λŠ₯κ³Ό 정확성에 영ν–₯을 λ―ΈμΉ©λ‹ˆλ‹€. 잘λͺ»λœ νŒ¨ν„΄μ€ 경쟁 μƒνƒœλ‚˜ ν¬λž˜μ‹œλ₯Ό μ΄ˆλž˜ν•©λ‹ˆλ‹€.

기법 4: κ²°κ³Ό 검증 및 μ™„λ£Œ

# 컀널 싀행을 λ‹¨κ³„λ³„λ‘œ μ‹€ν–‰ν•œ ν›„ μ΅œμ’… κ²°κ³Ό 확인
(cuda-gdb) print output[0]@4

좜λ ₯:

$11 = {10.0, 11.0, 12.0, 13.0}    # μ™„λ²½! 각 μš”μ†Œκ°€ 10 증가
# ν”„λ‘œκ·Έλž¨μ„ μ •μƒμ μœΌλ‘œ μ™„λ£Œ
(cuda-gdb) continue

좜λ ₯:

...ν”„λ‘œκ·Έλž¨ 좜λ ₯이 성곡 ν‘œμ‹œ...
# 디버거 μ’…λ£Œ
(cuda-gdb) exit

μ„€μ •λΆ€ν„° κ²°κ³ΌκΉŒμ§€ GPU 컀널 μ‹€ν–‰ 디버깅을 μ™„λ£Œν–ˆμŠ΅λ‹ˆλ‹€.

GPU 디버깅 μ—¬μ •: 핡심 톡찰

포괄적인 GPU 디버깅 νŠœν† λ¦¬μ–Όμ„ μ™„λ£Œν–ˆμŠ΅λ‹ˆλ‹€. 병렬 μ»΄ν“¨νŒ…μ— λŒ€ν•΄ λ°œκ²¬ν•œ λ‚΄μš©μž…λ‹ˆλ‹€:

병렬 싀행에 λŒ€ν•œ κΉŠμ€ 톡찰

  1. μŠ€λ ˆλ“œ μΈλ±μ‹±μ˜ μ‹€μ œ: thread_idx.xκ°€ 병렬 μŠ€λ ˆλ“œλ§ˆλ‹€ λ‹€λ₯Έ κ°’(0, 1, 2, 3…)을 κ°–λŠ” 것을 이둠이 μ•„λ‹Œ 직접 ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€

  2. λ©”λͺ¨λ¦¬ μ ‘κ·Ό νŒ¨ν„΄ νŒŒμ•…: 각 μŠ€λ ˆλ“œκ°€ a[thread_idx.x]μ—μ„œ 읽고 output[thread_idx.x]에 μ“°λ©°, 좩돌 없이 μ™„λ²½ν•œ 데이터 병렬성을 λ§Œλ“€μ–΄λƒ…λ‹ˆλ‹€

  3. 병렬 μ‹€ν–‰μ˜ 이해: 수천 개의 μŠ€λ ˆλ“œκ°€ λ™μΌν•œ 컀널 μ½”λ“œλ₯Ό λ™μ‹œμ— μ‹€ν–‰ν•˜λ©΄μ„œ 각각 μ„œλ‘œ λ‹€λ₯Έ 데이터 μš”μ†Œλ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€

  4. GPU λ©”λͺ¨λ¦¬ 계측 ꡬ쑰: 배열은 μ „μ—­ GPU λ©”λͺ¨λ¦¬μ— μžˆμ–΄ λͺ¨λ“  μŠ€λ ˆλ“œκ°€ μ ‘κ·Όν•  수 μžˆμ§€λ§Œ, μŠ€λ ˆλ“œλ³„ 인덱싱을 μ‚¬μš©ν•©λ‹ˆλ‹€

λͺ¨λ“  퍼즐에 μ μš©λ˜λŠ” 디버깅 기법

Puzzle 01λΆ€ν„° Puzzle 08, 그리고 κ·Έ μ΄ν›„κΉŒμ§€ 보편적으둜 μ μš©λ˜λŠ” 기법을 μŠ΅λ“ν–ˆμŠ΅λ‹ˆλ‹€:

  • CPU μΈ‘ 문제(μž₯치 μ„€μ •, λ©”λͺ¨λ¦¬ ν• λ‹Ή)λŠ” LLDB둜 μ‹œμž‘ν•©λ‹ˆλ‹€
  • GPU 컀널 문제(μŠ€λ ˆλ“œ λ™μž‘, λ©”λͺ¨λ¦¬ μ ‘κ·Ό)λŠ” CUDA-GDB둜 μ „ν™˜ν•©λ‹ˆλ‹€
  • νŠΉμ • μŠ€λ ˆλ“œλ‚˜ 데이터 쑰건에 μ§‘μ€‘ν•˜λ €λ©΄ 쑰건뢀 브레이크포인트λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€
  • 병렬 μ‹€ν–‰ νŒ¨ν„΄μ„ μ΄ν•΄ν•˜λ €λ©΄ μŠ€λ ˆλ“œ κ°„ 이동을 ν™œμš©ν•©λ‹ˆλ‹€
  • 경쟁 μƒνƒœμ™€ λ²”μœ„ 초과 였λ₯˜λ₯Ό 작으렀면 λ©”λͺ¨λ¦¬ μ ‘κ·Ό νŒ¨ν„΄μ„ ν™•μΈν•©λ‹ˆλ‹€

ν™•μž₯μ„±: 이 기법듀은 λ‹€μŒ λͺ¨λ“  μƒν™©μ—μ„œ λ™μΌν•˜κ²Œ μž‘λ™ν•©λ‹ˆλ‹€:

  • Puzzle 01: κ°„λ‹¨ν•œ λ§μ…ˆμ„ ν•˜λŠ” 4개 μš”μ†Œ λ°°μ—΄
  • Puzzle 08: μŠ€λ ˆλ“œ 동기화가 ν•„μš”ν•œ λ³΅μž‘ν•œ 곡유 λ©”λͺ¨λ¦¬ μ—°μ‚°
  • ν”„λ‘œλ•μ…˜ μ½”λ“œ: μ •κ΅ν•œ μ•Œκ³ λ¦¬μ¦˜μ„ μ‚¬μš©ν•˜λŠ” 백만 개 μš”μ†Œ λ°°μ—΄

ν•„μˆ˜ 디버깅 λͺ…λ Ήμ–΄ μ°Έμ‘°

디버깅 μ›Œν¬ν”Œλ‘œμš°λ₯Ό λ°°μ› μœΌλ‹ˆ, 일상적인 디버깅 μ„Έμ…˜μ—μ„œ μ“Έ λΉ λ₯Έ μ°Έμ‘° κ°€μ΄λ“œλ₯Ό λ“œλ¦½λ‹ˆλ‹€. 이 μ„Ήμ…˜μ„ λΆλ§ˆν¬ν•˜μ„Έμš”!

GDB λͺ…λ Ήμ–΄ μ•½μ–΄ (μ‹œκ°„ μ ˆμ•½!)

κ°€μž₯ 많이 μ‚¬μš©ν•˜λŠ” λ‹¨μΆ•ν‚€λ‘œ 더 λΉ λ₯Έ 디버깅:

약어전체 λͺ…λ Ήμ–΄κΈ°λŠ₯
rrunν”„λ‘œκ·Έλž¨ μ‹œμž‘/μ‹€ν–‰
ccontinueμ‹€ν–‰ 재개
nnextμŠ€ν… μ˜€λ²„ (같은 레벨)
sstepν•¨μˆ˜ λ‚΄λΆ€λ‘œ μ§„μž…
bbreak브레이크포인트 μ„€μ •
pprintλ³€μˆ˜ κ°’ 좜λ ₯
llistμ†ŒμŠ€ μ½”λ“œ ν‘œμ‹œ
qquit디버거 μ’…λ£Œ

μ˜ˆμ‹œ:

(cuda-gdb) r                    # 'run' λŒ€μ‹ 
(cuda-gdb) b 39                 # 'break 39' λŒ€μ‹ 
(cuda-gdb) p thread_id          # 'print thread_id' λŒ€μ‹ 
(cuda-gdb) n                    # 'next' λŒ€μ‹ 
(cuda-gdb) c                    # 'continue' λŒ€μ‹ 

⚑ Pro 팁: μ•½μ–΄λ₯Ό μ‚¬μš©ν•˜λ©΄ 디버깅 속도가 3-5λ°° λΉ¨λΌμ§‘λ‹ˆλ‹€!

LLDB λͺ…λ Ήμ–΄ (CPU 호슀트 μ½”λ“œ 디버깅)

μ–Έμ œ μ‚¬μš©: μž₯치 μ„€μ •, λ©”λͺ¨λ¦¬ ν• λ‹Ή, ν”„λ‘œκ·Έλž¨ 흐름, 호슀트 μΈ‘ ν¬λž˜μ‹œ 디버깅

μ‹€ν–‰ μ œμ–΄

(lldb) run                   # ν”„λ‘œκ·Έλž¨ μ‹€ν–‰
(lldb) continue              # μ‹€ν–‰ 재개 (별칭: c)
(lldb) step                  # ν•¨μˆ˜ λ‚΄λΆ€λ‘œ μ§„μž… (μ†ŒμŠ€ 레벨)
(lldb) next                  # ν•¨μˆ˜ κ±΄λ„ˆλ›°κΈ° (μ†ŒμŠ€ 레벨)
(lldb) finish                # ν˜„μž¬ ν•¨μˆ˜μ—μ„œ λ‚˜κ°€κΈ°

브레이크포인트 관리

(lldb) br set -n main        # main ν•¨μˆ˜μ— 브레이크포인트 μ„€μ •
(lldb) br set -n function_name     # μ–΄λ–€ ν•¨μˆ˜μ—λ“  브레이크포인트 μ„€μ •
(lldb) br list               # λͺ¨λ“  브레이크포인트 ν‘œμ‹œ
(lldb) br delete 1           # 브레이크포인트 #1 μ‚­μ œ
(lldb) br disable 1          # 브레이크포인트 #1 μž„μ‹œ λΉ„ν™œμ„±ν™”

λ³€μˆ˜ 검사

(lldb) print variable_name   # λ³€μˆ˜ κ°’ ν‘œμ‹œ
(lldb) print pointer[offset]        # 포인터 μ—­μ°Έμ‘°
(lldb) print array[0]@4      # 첫 4개 λ°°μ—΄ μš”μ†Œ ν‘œμ‹œ

CUDA-GDB λͺ…λ Ήμ–΄ (GPU 컀널 디버깅)

μ–Έμ œ μ‚¬μš©: GPU 컀널, μŠ€λ ˆλ“œ λ™μž‘, 병렬 μ‹€ν–‰, GPU λ©”λͺ¨λ¦¬ 문제 디버깅

GPU μƒνƒœ 검사

(cuda-gdb) info cuda threads    # λͺ¨λ“  GPU μŠ€λ ˆλ“œμ™€ μƒνƒœ ν‘œμ‹œ
(cuda-gdb) info cuda blocks     # λͺ¨λ“  μŠ€λ ˆλ“œ 블둝 ν‘œμ‹œ
(cuda-gdb) cuda kernel          # ν™œμ„± GPU 컀널 λ‚˜μ—΄

μŠ€λ ˆλ“œ 탐색 (κ°€μž₯ κ°•λ ₯ν•œ κΈ°λŠ₯!)

(cuda-gdb) cuda thread (0,0,0)  # νŠΉμ • μŠ€λ ˆλ“œ μ’Œν‘œλ‘œ μ „ν™˜
(cuda-gdb) cuda block (0,0)     # νŠΉμ • λΈ”λ‘μœΌλ‘œ μ „ν™˜
(cuda-gdb) cuda thread          # ν˜„μž¬ μŠ€λ ˆλ“œ μ’Œν‘œ ν‘œμ‹œ

μŠ€λ ˆλ“œλ³„ λ³€μˆ˜ 검사

# 둜컬 λ³€μˆ˜μ™€ ν•¨μˆ˜ νŒŒλΌλ―Έν„°:
(cuda-gdb) print i              # 둜컬 μŠ€λ ˆλ“œ 인덱슀 λ³€μˆ˜
(cuda-gdb) print output         # ν•¨μˆ˜ νŒŒλΌλ―Έν„° 포인터
(cuda-gdb) print a              # ν•¨μˆ˜ νŒŒλΌλ―Έν„° 포인터

GPU λ©”λͺ¨λ¦¬ μ ‘κ·Ό

# 둜컬 λ³€μˆ˜λ₯Ό μ‚¬μš©ν•œ λ°°μ—΄ 검사 (μ‹€μ œλ‘œ μž‘λ™ν•˜λŠ” 것):
(cuda-gdb) print array[i]       # 둜컬 λ³€μˆ˜λ₯Ό μ‚¬μš©ν•œ μŠ€λ ˆλ“œλ³„ λ°°μ—΄ μ ‘κ·Ό
(cuda-gdb) print array[0]@4     # μ—¬λŸ¬ μš”μ†Œ 보기: {{val1}, {val2}, {val3}, {val4}}

κ³ κΈ‰ GPU 디버깅

# λ©”λͺ¨λ¦¬ κ°μ‹œ
(cuda-gdb) watch array[i]     # λ©”λͺ¨λ¦¬ λ³€κ²½ μ‹œ 쀑단
(cuda-gdb) rwatch array[i]    # λ©”λͺ¨λ¦¬ 읽기 μ‹œ 쀑단

λΉ λ₯Έ μ°Έμ‘°: 디버깅 κ²°μ • 트리

πŸ€” μ–΄λ–€ μœ ν˜•μ˜ 문제λ₯Ό λ””λ²„κΉ…ν•˜κ³  μžˆλ‚˜μš”?

GPU μ½”λ“œ μ‹€ν–‰ 전에 ν”„λ‘œκ·Έλž¨μ΄ ν¬λž˜μ‹œ

β†’ LLDB 디버깅 μ‚¬μš©

pixi run mojo debug your_program.mojo

GPU 컀널이 잘λͺ»λœ κ²°κ³Ό 생성

β†’ 쑰건뢀 λΈŒλ ˆμ΄ν¬ν¬μΈνŠΈμ™€ ν•¨κ»˜ CUDA-GDB μ‚¬μš©

pixi run mojo debug --cuda-gdb --break-on-launch your_program.mojo

μ„±λŠ₯ λ¬Έμ œλ‚˜ 경쟁 μƒνƒœ

β†’ μž¬ν˜„μ„±μ„ μœ„ν•΄ λ°”μ΄λ„ˆλ¦¬ 디버깅 μ‚¬μš©

pixi run mojo build -O0 -g your_program.mojo -o debug_binary
pixi run mojo debug --cuda-gdb --break-on-launch debug_binary

GPU λ””λ²„κΉ…μ˜ 핡심을 λ°°μ› μŠ΅λ‹ˆλ‹€

GPU 디버깅 κΈ°μ΄ˆμ— λŒ€ν•œ 포괄적인 νŠœν† λ¦¬μ–Όμ„ μ™„λ£Œν–ˆμŠ΅λ‹ˆλ‹€. λ‹€μŒμ€ λ‹¬μ„±ν•œ λ‚΄μš©μž…λ‹ˆλ‹€:

μŠ΅λ“ν•œ 기술

닀쀑 레벨 디버깅 지식:

  • βœ… LLDB둜 CPU 호슀트 디버깅 - μž₯치 μ„€μ •, λ©”λͺ¨λ¦¬ ν• λ‹Ή, ν”„λ‘œκ·Έλž¨ 흐름 디버깅
  • βœ… CUDA-GDB둜 GPU 컀널 디버깅 - 병렬 μŠ€λ ˆλ“œ, GPU λ©”λͺ¨λ¦¬, 경쟁 μƒνƒœ 디버깅
  • βœ… μ†ŒμŠ€ vs λ°”μ΄λ„ˆλ¦¬ 디버깅 - 상황에 λ§žλŠ” 접근법 선택
  • βœ… pixi둜 ν™˜κ²½ 관리 - μΌκ΄€λ˜κ³  μ‹ λ’°ν•  수 μžˆλŠ” 디버깅 μ„€μ • 보μž₯

μ‹€μ œ 병렬 ν”„λ‘œκ·Έλž˜λ° 톡찰:

  • μŠ€λ ˆλ“œμ˜ μ‹€μ œ λ™μž‘ 확인 - 병렬 μŠ€λ ˆλ“œλ§ˆλ‹€ thread_idx.xκ°€ λ‹€λ₯Έ 값을 κ°–λŠ” 것을 직접 λͺ©κ²©ν–ˆμŠ΅λ‹ˆλ‹€
  • λ©”λͺ¨λ¦¬ 계측 ꡬ쑰 이해 - μ „μ—­ GPU λ©”λͺ¨λ¦¬, 곡유 λ©”λͺ¨λ¦¬, μŠ€λ ˆλ“œ 둜컬 λ³€μˆ˜λ₯Ό λ””λ²„κΉ…ν–ˆμŠ΅λ‹ˆλ‹€
  • μŠ€λ ˆλ“œ 탐색 ν•™μŠ΅ - 수천 개의 병렬 μŠ€λ ˆλ“œ 사이λ₯Ό 효율적으둜 μ΄λ™ν–ˆμŠ΅λ‹ˆλ‹€

μ΄λ‘ μ—μ„œ μ‹€μ „μœΌλ‘œ

GPU 디버깅에 λŒ€ν•΄ 읽기만 ν•œ 것이 μ•„λ‹ˆλΌ κ²½ν—˜ν–ˆμŠ΅λ‹ˆλ‹€:

  • μ‹€μ œ μ½”λ“œ 디버깅: μ‹€μ œ GPU μ‹€ν–‰μœΌλ‘œ Puzzle 01의 add_10 컀널을 λ””λ²„κΉ…ν–ˆμŠ΅λ‹ˆλ‹€
  • μ‹€μ œ 디버거 좜λ ₯ 확인: JIT 라벨이 뢙은 ν”„λ ˆμž„μ—μ„œ LLDB μ†ŒμŠ€ μ •μ§€, CUDA-GDB μŠ€λ ˆλ“œ μƒνƒœ, λ©”λͺ¨λ¦¬ μ£Όμ†Œλ₯Ό 직접 ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€
  • μ „λ¬Έ 도ꡬ μ‚¬μš©: ν”„λ‘œλ•μ…˜ GPU κ°œλ°œμ—μ„œ μ‚¬μš©ν•˜λŠ” 것과 λ™μΌν•œ CUDA-GDBλ₯Ό μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€
  • μ‹€μ œ μ‹œλ‚˜λ¦¬μ˜€ ν•΄κ²°: λ²”μœ„ 초과 μ ‘κ·Ό, 경쟁 μƒνƒœ, 컀널 μ‹€ν–‰ μ‹€νŒ¨ 문제λ₯Ό λ‹€λ€˜μŠ΅λ‹ˆλ‹€

디버깅 도ꡬ λͺ¨μŒ

λΉ λ₯Έ κ²°μ • κ°€μ΄λ“œ (항상 κ°€κΉŒμ΄ λ‘μ„Έμš”!):

문제 μœ ν˜•λ„κ΅¬λͺ…λ Ήμ–΄
GPU 전에 ν”„λ‘œκ·Έλž¨ ν¬λž˜μ‹œLLDBpixi run mojo debug program.mojo
GPU 컀널 문제CUDA-GDBpixi run mojo debug --cuda-gdb --break-on-launch program.mojo
경쟁 μƒνƒœCUDA-GDB + μŠ€λ ˆλ“œ 탐색(cuda-gdb) cuda thread (0,0,0)

ν•„μˆ˜ λͺ…λ Ήμ–΄ (일상 λ””λ²„κΉ…μš©):

# GPU μŠ€λ ˆλ“œ 검사
(cuda-gdb) info cuda threads          # λͺ¨λ“  μŠ€λ ˆλ“œ 보기
(cuda-gdb) cuda thread (0,0,0)        # μŠ€λ ˆλ“œ μ „ν™˜
(cuda-gdb) print i                    # 둜컬 μŠ€λ ˆλ“œ 인덱슀 (thread_idx.x λ“±κ°€)

# 슀마트 브레이크포인트 (GPU λ‚΄μž₯ λ³€μˆ˜κ°€ μž‘λ™ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ 둜컬 λ³€μˆ˜ μ‚¬μš©)
(cuda-gdb) break kernel if i == 0      # μŠ€λ ˆλ“œ 0에 집쀑
(cuda-gdb) break kernel if array[i] > 100  # 데이터 쑰건에 집쀑

# λ©”λͺ¨λ¦¬ 디버깅
(cuda-gdb) print array[i]              # 둜컬 λ³€μˆ˜λ₯Ό μ‚¬μš©ν•œ μŠ€λ ˆλ“œλ³„ 데이터
(cuda-gdb) print array[0]@4            # λ°°μ—΄ μ„Έκ·Έλ¨ΌνŠΈ: {{val1}, {val2}, {val3}, {val4}}

μš”μ•½

GPU λ””λ²„κΉ…μ—λŠ” 수천 개의 병렬 μŠ€λ ˆλ“œ, λ³΅μž‘ν•œ λ©”λͺ¨λ¦¬ 계측 ꡬ쑰, μ „λ¬Έ 도ꡬ가 κ΄€μ—¬ν•©λ‹ˆλ‹€. 이제 λ‹€μŒμ„ κ°–μΆ”κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€:

  • μ–΄λ–€ GPU ν”„λ‘œκ·Έλž¨μ—λ„ μ μš©ν•  수 μžˆλŠ” 체계적인 μ›Œν¬ν”Œλ‘œμš°
  • LLDB와 CUDA-GDB μ „λ¬Έ 도ꡬ에 λŒ€ν•œ μΉœμˆ™ν•¨
  • μ‹€μ œ 병렬 μ½”λ“œλ₯Ό λ””λ²„κΉ…ν•œ μ‹€μ „ κ²½ν—˜
  • λ³΅μž‘ν•œ 상황을 μ²˜λ¦¬ν•˜κΈ° μœ„ν•œ μ‹€μš©μ μΈ μ „λž΅
  • GPU 디버깅 과제λ₯Ό ν•΄κ²°ν•  기초

μΆ”κ°€ 자료

μ°Έκ³ : GPU λ””λ²„κΉ…μ—λŠ” 인내심과 체계적인 쑰사가 ν•„μš”ν•©λ‹ˆλ‹€. 이 νΌμ¦μ—μ„œ 닀룬 μ›Œν¬ν”Œλ‘œμš°μ™€ λͺ…λ Ήμ–΄λŠ” μ‹€μ œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ 마주치게 될 λ³΅μž‘ν•œ GPU 문제λ₯Ό λ””λ²„κΉ…ν•˜λŠ” κΈ°μ΄ˆκ°€ λ©λ‹ˆλ‹€.