๐ฎ๐ผโโ๏ธ ๋ฉ๋ชจ๋ฆฌ ์๋ฐ ํ์ง
๊ฐ์
ํ
์คํธ๊ฐ ํต๊ณผํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ฌ๋ GPU ํ๋ก๊ทธ๋จ์ ์กฐ์ฉํ ์์์ํฌ ์ ์๋ ๋ฉ๋ชจ๋ฆฌ ์๋ฐ์ ํ์งํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋๋ค. NVIDIA์ compute-sanitizer(pixi๋ฅผ ํตํด ์ฌ์ฉ ๊ฐ๋ฅ)์ memcheck ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ, GPU ์ฝ๋์์ ์์ธก ๋ถ๊ฐ๋ฅํ ๋์์ ์ผ์ผํฌ ์ ์๋ ์จ์ ๋ฉ๋ชจ๋ฆฌ ๋ฒ๊ทธ๋ฅผ ๋ฐ๊ฒฌํ๊ฒ ๋ฉ๋๋ค.
ํต์ฌ ํต์ฐฐ: GPU ํ๋ก๊ทธ๋จ์ ๋ถ๋ฒ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ์ ์ํํ๋ฉด์๋ ๋์์ โ์ฌ๋ฐ๋ฅธโ ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค์ด๋ผ ์ ์์ต๋๋ค.
์ ํ ํ์ต: Puzzle 4 LayoutTensor์ ๊ธฐ๋ณธ์ ์ธ GPU ๋ฉ๋ชจ๋ฆฌ ๊ฐ๋ ์ ๋ํ ์ดํด๊ฐ ํ์ํฉ๋๋ค.
์กฐ์ฉํ ๋ฉ๋ชจ๋ฆฌ ๋ฒ๊ทธ์ ๋ฐ๊ฒฌ
ํ ์คํธ๋ ํต๊ณผํ์ง๋ง, ์ฝ๋๊ฐ ์ ๋ง ์ฌ๋ฐ๋ฅธ ๊ฑธ๊น?
์ผํ ๋ฌดํดํด ๋ณด์ด๊ณ ์๋ฒฝํ๊ฒ ๋์ํ๋ ๋ฏํ ํ๋ก๊ทธ๋จ์ผ๋ก ์์ํด ๋ด ์๋ค (๊ฐ๋๊ฐ ์๋ Puzzle 04์ ๋๋ค):
fn add_10_2d(
output: LayoutTensor[dtype, layout, MutAnyOrigin],
a: LayoutTensor[dtype, layout, ImmutAnyOrigin],
size: UInt,
):
row = thread_idx.y
col = thread_idx.x
output[row, col] = a[row, col] + 10.0
์ ์ฒด ํ์ผ ๋ณด๊ธฐ: problems/p10/p10.mojo
์ด ํ๋ก๊ทธ๋จ์ ์ผ๋ฐ์ ์ผ๋ก ์คํํ๋ฉด, ๋ชจ๋ ๊ฒ์ด ์ ์์ผ๋ก ๋ณด์ ๋๋ค:
pixi run p10 --memory-bug
out shape: 2 x 2
Running memory bug example (bounds checking issue)...
out: HostBuffer([10.0, 11.0, 12.0, 13.0])
expected: HostBuffer([10.0, 11.0, 12.0, 13.0])
โ
Memory test PASSED! (memcheck may find bounds violations)
โ ํ ์คํธ ํต๊ณผ! ์ถ๋ ฅ์ด ์์ ๊ฒฐ๊ณผ์ ์๋ฒฝํ๊ฒ ์ผ์นํฉ๋๋ค. ์ฌ๊ฑด ์ข ๊ฒฐ, ๋ง์ฃ ?
์๋๋๋ค! compute-sanitizer๊ฐ ๋ฌด์์ ๋ณด์ฌ์ฃผ๋์ง ๋ด
์๋ค:
MODULAR_DEVICE_CONTEXT_MEMORY_MANAGER_SIZE_PERCENT=0 pixi run compute-sanitizer --tool memcheck mojo problems/p10/p10.mojo --memory-bug
์ฐธ๊ณ : MODULAR_DEVICE_CONTEXT_MEMORY_MANAGER_SIZE_PERCENT=0์ ๋๋ฐ์ด์ค ์ปจํ
์คํธ์ ๋ฒํผ ์บ์๋ฅผ ๋นํ์ฑํํ๋ ๋ช
๋ น์ค ํ๊ฒฝ ๋ณ์ ์ค์ ์
๋๋ค. ์ด ์ค์ ์ ์ผ๋ฐ์ ์ธ ์บ์ฑ ๋์์ ์ํด ์จ๊ฒจ์ง๋ ๊ฒฝ๊ณ ์๋ฐ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ๋ฌธ์ ๋ฅผ ๋๋ฌ๋ผ ์ ์์ต๋๋ค. (์ญ์ฃผ: ๋ฒํผ ์บ์๊ฐ ํ์ฑํ๋๋ฉด ํด์ ๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฆ์ ๋ฐํํ์ง ์๊ณ ์ฌ์ฌ์ฉ์ ์ํด ๋ณด๊ดํฉ๋๋ค. ์ด ๋๋ฌธ์ ๋ฒ์๋ฅผ ๋ฒ์ด๋ ์ ๊ทผ์ด ์์ง ์ ํจํ ์บ์ ์์ญ์ ๋ฟ์ ์ค๋ฅ๊ฐ ๋๋ฌ๋์ง ์์ ์ ์์ต๋๋ค. ๋นํ์ฑํํ๋ฉด ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ฆ์ ๋ฐํ๋์ด ์๋ฐ์ด ๊ฐ์ง๋ฉ๋๋ค.)
========= COMPUTE-SANITIZER
out shape: 2 x 2
Running memory bug example (bounds checking issue)...
========= Invalid __global__ read of size 4 bytes
========= at p10_add_10_2d_...+0x80
========= by thread (2,1,0) in block (0,0,0)
========= Access at 0xe0c000210 is out of bounds
========= and is 513 bytes after the nearest allocation at 0xe0c000000 of size 16 bytes
========= Invalid __global__ read of size 4 bytes
========= at p10_add_10_2d_...+0x80
========= by thread (0,2,0) in block (0,0,0)
========= Access at 0xe0c000210 is out of bounds
========= and is 513 bytes after the nearest allocation at 0xe0c000000 of size 16 bytes
========= Invalid __global__ read of size 4 bytes
========= at p10_add_10_2d_...+0x80
========= by thread (1,2,0) in block (0,0,0)
========= Access at 0xe0c000214 is out of bounds
========= and is 517 bytes after the nearest allocation at 0xe0c000000 of size 16 bytes
========= Invalid __global__ read of size 4 bytes
========= at p10_add_10_2d_...+0x80
========= by thread (2,2,0) in block (0,0,0)
========= Access at 0xe0c000218 is out of bounds
========= and is 521 bytes after the nearest allocation at 0xe0c000000 of size 16 bytes
========= Program hit CUDA_ERROR_LAUNCH_FAILED (error 719) due to "unspecified launch failure" on CUDA API call to cuStreamSynchronize.
========= Program hit CUDA_ERROR_LAUNCH_FAILED (error 719) due to "unspecified launch failure" on CUDA API call to cuEventCreate.
========= Program hit CUDA_ERROR_LAUNCH_FAILED (error 719) due to "unspecified launch failure" on CUDA API call to cuMemFreeAsync.
========= ERROR SUMMARY: 7 errors
๋ชจ๋ ํ ์คํธ๋ฅผ ํต๊ณผํ์์๋ ํ๋ก๊ทธ๋จ์๋ ์ด 7๊ฐ์ ์ค๋ฅ๊ฐ ์์ต๋๋ค:
- 4๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์๋ฐ (
Invalid __global__ read) - 3๊ฐ์ ๋ฐํ์ ์ค๋ฅ (๋ฉ๋ชจ๋ฆฌ ์๋ฐ์ผ๋ก ์ธํด ๋ฐ์)
์จ๊ฒจ์ง ๋ฒ๊ทธ ์ดํดํ๊ธฐ
๊ทผ๋ณธ ์์ธ ๋ถ์
๋ฌธ์ :
- ํ ์ ํฌ๊ธฐ: 2ร2 (์ ํจํ ์ธ๋ฑ์ค: 0, 1)
- ์ค๋ ๋ ๊ทธ๋ฆฌ๋: 3ร3 (์ค๋ ๋ ์ธ๋ฑ์ค: 0, 1, 2)
- ๋ฒ์ ์ด๊ณผ ์ค๋ ๋:
(2,1),(0,2),(1,2),(2,2)๊ฐ ์๋ชป๋ ๋ฉ๋ชจ๋ฆฌ์ ์ ๊ทผ - ๊ฒฝ๊ณ ๊ฒ์ฌ ๋๋ฝ: ํ
์ ์ฐจ์์ ๋ํ
thread_idx๊ฒ์ฆ์ด ์์
7๊ฐ ์ค๋ฅ ์ ์ฒด ์ดํดํ๊ธฐ
4๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์๋ฐ:
- ๊ฐ ๋ฒ์ ์ด๊ณผ ์ค๋ ๋
(2,1),(0,2),(1,2),(2,2)๊ฐInvalid __global__ read๋ฅผ ๋ฐ์์ํด
3๊ฐ์ CUDA ๋ฐํ์ ์ค๋ฅ:
- ์ปค๋ ์คํ ์คํจ๋ก ์ธํด
cuStreamSynchronize์คํจ - ์ ๋ฆฌ ๊ณผ์ ์์
cuEventCreate์คํจ - ๋ฉ๋ชจ๋ฆฌ ํด์ ๊ณผ์ ์์
cuMemFreeAsync์คํจ
ํต์ฌ ํต์ฐฐ: ๋ฉ๋ชจ๋ฆฌ ์๋ฐ์ ์ฐ์ ํจ๊ณผ๋ฅผ ์ผ์ผํต๋๋ค - ํ๋์ ์๋ชป๋ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ์ด ์ฌ๋ฌ ํ์ CUDA API ์คํจ๋ฅผ ์ผ๊ธฐํฉ๋๋ค.
๊ทธ๋ผ์๋ ํ ์คํธ๊ฐ ํต๊ณผํ ์ด์ :
- ์ ํจํ ์ค๋ ๋
(0,0),(0,1),(1,0),(1,1)์ด ์ฌ๋ฐ๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋กํจ - ํ ์คํธ๊ฐ ์ ํจํ ์ถ๋ ฅ ์์น๋ง ๊ฒ์ฌํจ
- ๋ฒ์ ์ด๊ณผ ์ ๊ทผ์ด ํ๋ก๊ทธ๋จ์ ์ฆ์ ํฌ๋์์ํค์ง ์์
๋ฏธ์ ์ ๋์ ์ดํดํ๊ธฐ
๋ฏธ์ ์ ๋์์ด๋?
๋ฏธ์ ์ ๋์(Undefined Behavior, UB) ์ ํ๋ก๊ทธ๋จ์ด ์ธ์ด ๋ช ์ธ์ ์ ์๋์ง ์์ ์ฐ์ฐ์ ์ํํ ๋ ๋ฐ์ํฉ๋๋ค. ๋ฒ์ ์ด๊ณผ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ์ด ๋ํ์ ์ธ ์์ ๋๋ค.
๋ฏธ์ ์ ๋์์ ์ฃผ์ ํน์ฑ:
- ํ๋ก๊ทธ๋จ์ด ๋ง ๊ทธ๋๋ก ๋ฌด์จ ์ง์ด๋ ํ ์ ์์: ํฌ๋์, ์๋ชป๋ ๊ฒฐ๊ณผ, ์ ์ ๋์ํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ธฐ, ๋ฉ๋ชจ๋ฆฌ ์์
- ์ด๋ค ๋ณด์ฅ๋ ์์: ์ปดํ์ผ๋ฌ, ํ๋์จ์ด, ๋๋ผ์ด๋ฒ, ์ฌ์ง์ด ์คํํ ๋๋ง๋ค ๋์์ด ๋ฌ๋ผ์ง ์ ์์
๋ฏธ์ ์ ๋์์ด ํนํ ์ํํ ์ด์
์ ํ์ฑ ๋ฌธ์ :
- ์์ธก ๋ถ๊ฐ๋ฅํ ๊ฒฐ๊ณผ: ํ ์คํธ ์ค์๋ ๋์ํ๋ค๊ฐ ํ๋ก๋์ ์์ ์คํจํ ์ ์์
- ๋น๊ฒฐ์ ์ ๋์: ๊ฐ์ ์ฝ๋๊ฐ ๋ค๋ฅธ ์คํ์์ ๋ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ผ ์ ์์
- ์กฐ์ฉํ ์์: ๋ฏธ์ ์ ๋์์ ๊ฐ์์ ์ธ ์ค๋ฅ ์์ด ๋ฐ์ดํฐ๋ฅผ ์์์ํฌ ์ ์์
- ์ปดํ์ผ๋ฌ ์ต์ ํ: ์ปดํ์ผ๋ฌ๋ ๋ฏธ์ ์ ๋์์ด ์๋ค๊ณ ๊ฐ์ ํ๊ณ ์์์น ๋ชปํ ๋ฐฉ์์ผ๋ก ์ต์ ํํ ์ ์์
๋ณด์ ์ทจ์ฝ์ :
- ๋ฒํผ ์ค๋ฒํ๋ก์ฐ: ์์คํ ํ๋ก๊ทธ๋๋ฐ์์ ๋ณด์ ๊ณต๊ฒฉ์ ๊ณ ์ ์ ์ธ ์์ธ
- ๋ฉ๋ชจ๋ฆฌ ์์: ๊ถํ ์์น์ด๋ ์ฝ๋ ์ธ์ ์ ๊ณต๊ฒฉ์ผ๋ก ์ด์ด์ง ์ ์์
- ์ ๋ณด ์ ์ถ: ๋ฒ์๋ฅผ ๋ฒ์ด๋ ์ฝ๊ธฐ๋ก ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๊ฐ ๋ ธ์ถ๋ ์ ์์
- ์ ์ด ํ๋ฆ ํ์ด์ฌํน: ๋ฏธ์ ์ ๋์์ ์ ์ฉํด ํ๋ก๊ทธ๋จ ์คํ ํ๋ฆ์ ํ์ทจํ ์ ์์
GPU ํน์ ์ ๋ฏธ์ ์ ๋์ ์ํ์ฑ
๋๊ท๋ชจ ์ํฅ:
- ์ค๋ ๋ ๋ถ๊ธฐ: ํ ์ค๋ ๋์ ๋ฏธ์ ์ ๋์์ด ์ ์ฒด ์ํ(32๊ฐ ์ค๋ ๋)์ ์ํฅ์ ์ค ์ ์์
- ๋ฉ๋ชจ๋ฆฌ ๋ณํฉ: ๋ฒ์ ์ด๊ณผ ์ ๊ทผ์ด ์ธ์ ์ค๋ ๋์ ๋ฐ์ดํฐ๋ฅผ ์์์ํฌ ์ ์์
- ์ปค๋ ์คํจ: ๋ฏธ์ ์ ๋์์ด GPU ์ปค๋ ์ ์ฒด๋ฅผ ์์ ํ ๋ง๊ฐ๋จ๋ฆด ์ ์์
ํ๋์จ์ด ์ฐจ์ด:
- ๋ค๋ฅธ GPU ์ํคํ ์ฒ: ๋ฏธ์ ์ ๋์์ด ๋ค๋ฅธ GPU ๋ชจ๋ธ์์ ๋ค๋ฅด๊ฒ ๋ํ๋ ์ ์์
- ๋๋ผ์ด๋ฒ ์ฐจ์ด: ๊ฐ์ ๋ฏธ์ ์ ๋์์ด ๋๋ผ์ด๋ฒ ๋ฒ์ ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ๋์ํ ์ ์์
- ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์ ๋ณ๊ฒฝ: GPU ๋ฉ๋ชจ๋ฆฌ ํ ๋น ํจํด์ ๋ฐ๋ผ ๋ฏธ์ ์ ๋์์ด ๋ค๋ฅด๊ฒ ๋ํ๋ ์ ์์
๋ฉ๋ชจ๋ฆฌ ์๋ฐ ์์ ํ๊ธฐ
ํด๊ฒฐ์ฑ
Puzzle 04์์ ๋ณธ ๊ฒ์ฒ๋ผ, ๋ค์๊ณผ ๊ฐ์ด ๊ฒฝ๊ณ ๊ฒ์ฌ๋ฅผ ํด์ผ ํฉ๋๋ค:
fn add_10_2d(
output: LayoutTensor[dtype, layout, MutAnyOrigin],
a: LayoutTensor[dtype, layout, MutAnyOrigin],
size: UInt,
):
row = thread_idx.y
col = thread_idx.x
if col < size and row < size:
output[row, col] = a[row, col] + 10.0
ํด๊ฒฐ์ฑ ์ ๊ฐ๋จํฉ๋๋ค: ๋ฉ๋ชจ๋ฆฌ์ ์ ๊ทผํ๊ธฐ ์ ์ ํญ์ ์ค๋ ๋ ์ธ๋ฑ์ค๋ฅผ ๋ฐ์ดํฐ ์ฐจ์์ ๋ํด ๊ฒ์ฆํ์ธ์.
compute-sanitizer๋ก ๊ฒ์ฆ
# p10.mojo ๋ณต์ฌ๋ณธ์์ ๊ฒฝ๊ณ ๊ฒ์ฌ๋ฅผ ์์ ํ ํ ์คํ:
MODULAR_DEVICE_CONTEXT_MEMORY_MANAGER_SIZE_PERCENT=0 pixi run compute-sanitizer --tool memcheck mojo problems/p10/p10.mojo --memory-bug
========= COMPUTE-SANITIZER
out shape: 2 x 2
Running memory bug example (bounds checking issue)...
out: HostBuffer([10.0, 11.0, 12.0, 13.0])
expected: HostBuffer([10.0, 11.0, 12.0, 13.0])
โ
Memory test PASSED! (memcheck may find bounds violations)
========= ERROR SUMMARY: 0 errors
โ ์ฑ๊ณต: ๋ฉ๋ชจ๋ฆฌ ์๋ฐ์ด ํ์ง๋์ง ์์์ต๋๋ค!
ํต์ฌ ํ์ต ํฌ์ธํธ
์๋ ๊ฒฝ๊ณ ๊ฒ์ฌ๊ฐ ์ค์ํ ์ด์
- ๋ช ํ์ฑ: ์ฝ๋์์ ์์ ์๊ตฌ์ฌํญ์ ๋ช ์์ ์ผ๋ก ํํ
- ์ ์ด: ๋ฒ์ ์ด๊ณผ ์ผ์ด์ค์์ ์ ํํ ์ด๋ค ์ผ์ด ์ผ์ด๋ ์ง ์ง์ ๊ฒฐ์
- ๋๋ฒ๊น : ๋ฉ๋ชจ๋ฆฌ ์๋ฐ์ด ๋ฐ์ํ ๋ ์ถ๋ก ํ๊ธฐ ์ฌ์
GPU ๋ฉ๋ชจ๋ฆฌ ์์ ๊ท์น
- ํญ์ ์ค๋ ๋ ์ธ๋ฑ์ค๋ฅผ ๊ฒ์ฆํ์ฌ ๋ฐ์ดํฐ ์ฐจ์๊ณผ ๋น๊ต
- ๋ฏธ์ ์ ๋์์ ์ด๋ค ๋๊ฐ๋ฅผ ์น๋ฅด๋๋ผ๋ ํผํ๊ธฐ - ๋ฒ์ ์ด๊ณผ ์ ๊ทผ์ ๋ฏธ์ ์ ๋์์ด๋ฉฐ ๋ชจ๋ ๊ฒ์ ๋ง๊ฐ๋จ๋ฆด ์ ์์
- ๊ฐ๋ฐ๊ณผ ํ ์คํธ ์ค compute-sanitizer ์ฌ์ฉ
- ๋ฉ๋ชจ๋ฆฌ ๊ฒ์ฌ ์์ด โ๋์ํ๋คโ๊ณ ์ ๋ ๊ฐ์ ํ์ง ์๊ธฐ
- ๋ค์ํ ๊ทธ๋ฆฌ๋/๋ธ๋ก ๊ตฌ์ฑ์ผ๋ก ํ ์คํธํ์ฌ ์ผ๊ด์ฑ ์์ด ๋ํ๋๋ ๋ฏธ์ ์ ๋์ ํฌ์ฐฉ
compute-sanitizer ๋ชจ๋ฒ ์ฌ๋ก
MODULAR_DEVICE_CONTEXT_MEMORY_MANAGER_SIZE_PERCENT=0 pixi run compute-sanitizer --tool memcheck mojo your_code.mojo
์ฐธ๊ณ : ์๋ํ์ด์ ์ถ๋ ฅ์์ Mojo ๋ฐํ์ ๊ฒฝ๊ณ ๋ฅผ ๋ณผ ์ ์์ต๋๋ค. ์ค์ ๋ฉ๋ชจ๋ฆฌ ์๋ฐ์ ํ์ธํ๋ ค๋ฉด ========= Invalid์ ========= ERROR SUMMARY ๋ผ์ธ์ ์ง์คํ์ธ์.