๐ง ์ํ ๋ ์ธ๊ณผ SIMT ์คํ
์ํ ํ๋ก๊ทธ๋๋ฐ vs SIMD ๋ฉํ ๋ชจ๋ธ
์ํ๋ ๋ฌด์์ธ๊ฐ?
์ํ๋ 32๊ฐ(๋๋ 64๊ฐ)์ GPU ์ค๋ ๋๊ฐ ์๋ก ๋ค๋ฅธ ๋ฐ์ดํฐ์ ๋ํด ๋์ผํ ๋ช ๋ น์ ๋์์ ์คํํ๋ ๊ทธ๋ฃน์ ๋๋ค. ๊ฐ ์ค๋ ๋๊ฐ ๋ฒกํฐ ํ๋ก์ธ์์ โ๋ ์ธโ ์ญํ ์ ํ๋ ๋๊ธฐํ๋ ๋ฒกํฐ ์ ๋์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค.
๊ฐ๋จํ ์์:
from gpu.primitives.warp import sum
# ์ํ ๋ด 32๊ฐ ์ค๋ ๋๊ฐ ๋์์ ์คํ:
var my_value = input[my_thread_id] # ๊ฐ ์ค๋ ๋๊ฐ ์๋ก ๋ค๋ฅธ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ด
var warp_total = sum(my_value) # ๋ชจ๋ ์ค๋ ๋๊ฐ ํ๋์ ํฉ๊ณ์ ๊ธฐ์ฌ
๋ฌด์จ ์ผ์ด ์ผ์ด๋ ๊ฑธ๊น์? 32๊ฐ์ ๊ฐ๋ณ ์ค๋ ๋๊ฐ ๋ณต์กํ ์กฐ์จ์ ํ๋ ๋์ , ์ํ๊ฐ ์๋์ผ๋ก ๋๊ธฐํํ์ฌ ํ๋์ ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค์ด๋์ต๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก SIMT(Single Instruction, Multiple Thread) ์คํ์ ๋๋ค.
SIMT vs SIMD ๋น๊ต
CPU ๋ฒกํฐ ํ๋ก๊ทธ๋๋ฐ(SIMD)์ ์ต์ํ๋ค๋ฉด, GPU ์ํ๋ ๋น์ทํ์ง๋ง ํต์ฌ์ ์ธ ์ฐจ์ด๊ฐ ์์ต๋๋ค:
| ๊ด์ | CPU SIMD (์: AVX) | GPU ์ํ (SIMT) |
|---|---|---|
| ํ๋ก๊ทธ๋๋ฐ ๋ชจ๋ธ | ๋ช ์์ ๋ฒกํฐ ์ฐ์ฐ | ์ค๋ ๋ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ |
| ๋ฐ์ดํฐ ํญ | ๊ณ ์ (256/512 ๋นํธ) | ์ ์ฐ (32/64 ์ค๋ ๋) |
| ๋๊ธฐํ | ๋ช ๋ น ๋ด ์์์ | ์ํ ๋ด ์์์ |
| ํต์ | ๋ฉ๋ชจ๋ฆฌ/๋ ์ง์คํฐ ๊ฒฝ์ | ์ ํ ์ฐ์ฐ ๊ฒฝ์ |
| ๋ถ๊ธฐ ์ฒ๋ฆฌ | ํด๋น ์์ | ํ๋์จ์ด ๋ง์คํน |
| ์์ | a + b | sum(thread_value) |
CPU SIMD ๋ฐฉ์ (C++ intrinsics):
// ๋ช
์์ ๋ฒกํฐ ์ฐ์ฐ - 8๊ฐ์ float๋ฅผ ๋ณ๋ ฌ๋ก
__m256 result = _mm256_add_ps(a, b); // 8์์ ๋์์ ๋ง์
CPU SIMD ๋ฐฉ์ (Mojo):
# Mojo์์ SIMD๋ ์ผ๊ธ ์๋ฏผ ํ์
์ด๋ฏ๋ก a, b๊ฐ SIMD ํ์
์ด๋ฉด
# ๋ง์
์ด ๋ณ๋ ฌ๋ก ์ํ๋ฉ๋๋ค
var result = a + b # 8์์ ๋์์ ๋ง์
GPU SIMT ๋ฐฉ์ (Mojo):
# ์ค๋ ๋ ๊ธฐ๋ฐ ์ฝ๋๊ฐ ๋ฒกํฐ ์ฐ์ฐ์ผ๋ก ๋ณํ๋ฉ๋๋ค
from gpu.primitives.warp import sum
var my_data = input[thread_id] # ๊ฐ ์ค๋ ๋๊ฐ ์๊ธฐ ์์๋ฅผ ๊ฐ์ ธ์ด
var partial = my_data * coefficient # ๋ชจ๋ ์ค๋ ๋๊ฐ ๋์์ ๊ณ์ฐ
var total = sum(partial) # ํ๋์จ์ด๊ฐ ํฉ์ฐ์ ์กฐ์จ
์ํ๋ฅผ ๊ฐ๋ ฅํ๊ฒ ๋ง๋๋ ํต์ฌ ๊ฐ๋
1. ๋ ์ธ ์๋ณ: ๊ฐ ์ค๋ ๋๋ ์ฌ์ค์ ๋น์ฉ ์์ด ์ ๊ทผํ ์ ์๋ โ๋ ์ธ IDโ (0~31)๋ฅผ ๊ฐ์ต๋๋ค
var my_lane = lane_id() # ํ๋์จ์ด ๋ ์ง์คํฐ๋ฅผ ์ฝ์ ๋ฟ
2. ์์์ ๋๊ธฐํ: ์ํ ๋ด์์ ๋ฐฐ๋ฆฌ์ด๊ฐ ํ์ ์์ต๋๋ค
# ๊ทธ๋ฅ ๋์ - ๋ชจ๋ ์ค๋ ๋๊ฐ ์๋์ผ๋ก ๋๊ธฐํ
var sum = sum(my_contribution)
3. ํจ์จ์ ์ธ ํต์ : ๋ฉ๋ชจ๋ฆฌ ์์ด๋ ์ค๋ ๋ ๊ฐ ๋ฐ์ดํฐ ๊ณต์ ๊ฐ ๊ฐ๋ฅํฉ๋๋ค
# ๋ ์ธ 0์ ๊ฐ์ ๋ค๋ฅธ ๋ชจ๋ ๋ ์ธ์ผ๋ก ์ ๋ฌ
var broadcasted = shuffle_idx(my_value, 0)
ํต์ฌ ํต์ฐฐ: SIMT๋ฅผ ์ฌ์ฉํ๋ฉด ์์ฐ์ค๋ฌ์ด ์ค๋ ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด์๋ ํจ์จ์ ์ธ ๋ฒกํฐ ์ฐ์ฐ์ผ๋ก ์คํํ ์ ์์ด, ์ค๋ ๋ ํ๋ก๊ทธ๋๋ฐ์ ํธ๋ฆฌํจ๊ณผ ๋ฒกํฐ ์ฒ๋ฆฌ์ ์ฑ๋ฅ์ ๋ชจ๋ ์ป์ ์ ์์ต๋๋ค.
GPU ์คํ ๊ณ์ธต ๊ตฌ์กฐ์์ ์ํ์ ์์น
์ํ๊ฐ ์ ์ฒด GPU ์คํ ๋ชจ๋ธ๊ณผ ์ด๋ป๊ฒ ์ฐ๊ฒฐ๋๋์ง ์์ธํ ์์๋ณด๋ ค๋ฉด GPU ์ค๋ ๋ฉ vs SIMD ๊ฐ๋ ์ ์ฐธ๊ณ ํ์ธ์. ์ํ์ ์์น๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
GPU ๋๋ฐ์ด์ค
โโโ ๊ทธ๋ฆฌ๋ (์ ์ฒด ๋ฌธ์ )
โ โโโ ๋ธ๋ก 1 (์ค๋ ๋ ๊ทธ๋ฃน, ๊ณต์ ๋ฉ๋ชจ๋ฆฌ)
โ โ โโโ ์ํ 1 (32 ์ค๋ ๋, ๋ก์คํ
์คํ) โ ์ด ๋ ๋ฒจ
โ โ โ โโโ ์ค๋ ๋ 1 โ SIMD ์ฐ์ฐ
โ โ โ โโโ ์ค๋ ๋ 2 โ SIMD ์ฐ์ฐ
โ โ โ โโโ ... (์ด 32๊ฐ ์ค๋ ๋)
โ โ โโโ ์ํ 2 (32 ์ค๋ ๋)
โ โโโ ๋ธ๋ก 2 (๋
๋ฆฝ์ ์ธ ๊ทธ๋ฃน)
์ํ ํ๋ก๊ทธ๋๋ฐ์ โ์ํ ๋ ๋ฒจโ์์ ๋์ํฉ๋๋ค - ๋จ์ผ ์ํ ๋ด์ 32๊ฐ ์ค๋ ๋๋ฅผ ๋ชจ๋ ์กฐ์จํ๋ ์ฐ์ฐ์ ๋ค๋ฃจ๋ฉฐ, ๊ทธ๋ ์ง ์์ผ๋ฉด ๋ณต์กํ ๊ณต์ ๋ฉ๋ชจ๋ฆฌ ์กฐ์จ์ด ํ์ํ sum() ๊ฐ์ ๊ฐ๋ ฅํ ๊ธฐ๋ณธ ์์๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ด ๋ฉํ ๋ชจ๋ธ์ ๋ฌธ์ ๊ฐ ์ํ ์ฐ์ฐ์ ์์ฐ์ค๋ฝ๊ฒ ๋งคํ๋๋ ๊ฒฝ์ฐ์ ๊ธฐ์กด์ ๊ณต์ ๋ฉ๋ชจ๋ฆฌ ๋ฐฉ์์ด ํ์ํ ๊ฒฝ์ฐ๋ฅผ ๊ตฌ๋ถํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
์ํ ํ๋ก๊ทธ๋๋ฐ์ ํ๋์จ์ด ๊ธฐ๋ฐ
Single Instruction, Multiple Thread(SIMT) ์คํ์ ์ดํดํ๋ ๊ฒ์ ํจ๊ณผ์ ์ธ ์ํ ํ๋ก๊ทธ๋๋ฐ์ ํ์์ ์ ๋๋ค. ์ด๊ฒ์ ๋จ์ํ ์ํํธ์จ์ด ์ถ์ํ๊ฐ ์๋๋ผ, GPU ํ๋์จ์ด๊ฐ ์ค๋ฆฌ์ฝ ์์ค์์ ์ค์ ๋ก ์๋ํ๋ ๋ฐฉ์์ ๋๋ค.
SIMT ์คํ์ด๋?
SIMT๋ ์ํ ๋ด์์ ๋ชจ๋ ์ค๋ ๋๊ฐ ์๋ก ๋ค๋ฅธ ๋ฐ์ดํฐ์ ๋ํด ๊ฐ์ ๋ช ๋ น์ ๋์์ ์คํํ๋ค๋ ๋ป์ ๋๋ค. ์ด๋ ์์ ํ ๋ค๋ฅธ ๋ช ๋ น์ ๋ ๋ฆฝ์ ์ผ๋ก ์คํํ ์ ์๋ CPU ์ค๋ ๋์ ๊ทผ๋ณธ์ ์ผ๋ก ๋ค๋ฆ ๋๋ค.
CPU vs GPU ์คํ ๋ชจ๋ธ
| ๊ด์ | CPU (MIMD) | GPU ์ํ (SIMT) |
|---|---|---|
| ๋ช ๋ น ๋ชจ๋ธ | Multiple Instructions, Multiple Data | Single Instruction, Multiple Thread |
| Core 1 | add r1, r2 | add r1, r2 |
| Core 2 | load r3, [mem] | add r1, r2 (๋์ผ ๋ช
๋ น) |
| Core 3 | branch loop | add r1, r2 (๋์ผ ๋ช
๋ น) |
| โฆ Core 32 | ๋ค๋ฅธ ๋ช
๋ น | add r1, r2 (๋์ผ ๋ช
๋ น) |
| ์คํ ๋ฐฉ์ | ๋ ๋ฆฝ์ , ๋น๋๊ธฐ | ๋๊ธฐํ, ๋ก์คํ |
| ์ค์ผ์ค๋ง | ๋ณต์ก, OS ๊ด๋ฆฌ | ๋จ์, ํ๋์จ์ด ๊ด๋ฆฌ |
| ๋ฐ์ดํฐ | ๋ ๋ฆฝ์ ์ธ ๋ฐ์ดํฐ ์ธํธ | ์๋ก ๋ค๋ฅธ ๋ฐ์ดํฐ, ๊ฐ์ ์ฐ์ฐ |
GPU ์ํ ์คํ ํจํด:
- ๋ช
๋ น: 32๊ฐ ๋ ์ธ ๋ชจ๋ ๋์ผ:
add r1, r2 - ๋ ์ธ 0:
Data0์ ์ฐ์ฐ โResult0 - ๋ ์ธ 1:
Data1์ ์ฐ์ฐ โResult1 - ๋ ์ธ 2:
Data2์ ์ฐ์ฐ โResult2 - โฆ (๋ชจ๋ ๋ ์ธ์ด ๋์์ ์คํ)
- ๋ ์ธ 31:
Data31์ ์ฐ์ฐ โResult31
ํต์ฌ ํต์ฐฐ: ๋ชจ๋ ๋ ์ธ์ด ์๋ก ๋ค๋ฅธ ๋ฐ์ดํฐ์ ๋ํด ๊ฐ์ ๋ช ๋ น์ ๋์์ ์คํํฉ๋๋ค.
SIMT๊ฐ GPU์ ์ ํฉํ ์ด์
GPU๋ ์ง์ฐ ์๊ฐ์ด ์๋ ์ฒ๋ฆฌ๋์ ์ต์ ํ๋์ด ์์ต๋๋ค. SIMT๊ฐ ๊ฐ๋ฅํ๊ฒ ํ๋ ๊ฒ๋ค:
- ํ๋์จ์ด ๋จ์ํ: ํ๋์ ๋ช ๋ น ๋์ฝ๋๊ฐ 32๊ฐ ๋๋ 64๊ฐ ์ค๋ ๋๋ฅผ ์ฒ๋ฆฌ
- ์คํ ํจ์จ์ฑ: ์ํ ๋ด ์ค๋ ๋ ๊ฐ ๋ณต์กํ ์ค์ผ์ค๋ง ๋ถํ์
- ๋ฉ๋ชจ๋ฆฌ ๋์ญํญ: ๋ณํฉ๋ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ํจํด
- ์ ๋ ฅ ํจ์จ์ฑ: ๋ ์ธ ์ ์ฒด์ ๊ฑธ์ณ ์ ์ด ๋ก์ง ๊ณต์
์ํ ์คํ ๋ฉ์ปค๋์ฆ
๋ ์ธ ๋ฒํธ์ ์๋ณ
์ํ ๋ด ๊ฐ ์ค๋ ๋๋ 0๋ถํฐ WARP_SIZE-1๊น์ง์ ๋ ์ธ ID๋ฅผ ๊ฐ์ต๋๋ค:
from gpu import lane_id
from gpu.primitives.warp import WARP_SIZE
# ์ปค๋ ํจ์ ๋ด์์:
my_lane = lane_id() # 0-31 (NVIDIA/RDNA) ๋๋ 0-63 (CDNA) ๋ฐํ
ํต์ฌ ํต์ฐฐ: lane_id()๋ ๋น์ฉ์ด ์์ต๋๋ค - ๊ฐ์ ๊ณ์ฐํ๋ ๊ฒ์ด ์๋๋ผ ํ๋์จ์ด ๋ ์ง์คํฐ๋ฅผ ์ฝ์ ๋ฟ์
๋๋ค.
์ํ ๋ด ๋๊ธฐํ
SIMT์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ์ธก๋ฉด: ์์์ ๋๊ธฐํ.
# thread_idx.x < WARP_SIZE์ธ ๊ฒฝ์ฐ์ ์์
# 1. ๊ธฐ์กด ๊ณต์ ๋ฉ๋ชจ๋ฆฌ ๋ฐฉ์:
shared[thread_idx.x] = partial_result
barrier() # ๋ช
์์ ๋๊ธฐํ ํ์
var total = shared[0] + shared[1] + ... + shared[WARP_SIZE] # ํฉ์ฐ ๋ฆฌ๋์
# 2. ์ํ ๋ฐฉ์:
from gpu.primitives.warp import sum
var total = sum(partial_result) # ์์์ ๋๊ธฐํ!
์ ๋ฐฐ๋ฆฌ์ด๊ฐ ํ์ ์์๊น์? ๋ชจ๋ ๋ ์ธ์ด ๊ฐ ๋ช
๋ น์ ์ ํํ ๊ฐ์ ์์ ์ ์คํํ๊ธฐ ๋๋ฌธ์
๋๋ค. sum()์ด ์์๋ ๋, ๋ชจ๋ ๋ ์ธ์ ์ด๋ฏธ partial_result ๊ณ์ฐ์ ๋ง์น ์ํ์
๋๋ค.
์ํ ๋ถ๊ธฐ์ ์๋ ด
์กฐ๊ฑด ์ฝ๋์์ ๋ฌด์จ ์ผ์ด ์ผ์ด๋ ๊น?
if lane_id() % 2 == 0:
# ์ง์ ๋ ์ธ์ด ์ด ๊ฒฝ๋ก๋ฅผ ์คํ
result = compute_even()
else:
# ํ์ ๋ ์ธ์ด ์ด ๊ฒฝ๋ก๋ฅผ ์คํ
result = compute_odd()
# ๋ชจ๋ ๋ ์ธ์ด ์ฌ๊ธฐ์ ์๋ ด
ํ๋์จ์ด ๋์ ๋จ๊ณ:
| ๋จ๊ณ | ํ์ด์ฆ | ํ์ฑ ๋ ์ธ | ๋๊ธฐ ๋ ์ธ | ํจ์จ | ์ฑ๋ฅ ๋น์ฉ |
|---|---|---|---|---|---|
| 1 | ์กฐ๊ฑด ํ๊ฐ | 32๊ฐ ๋ ์ธ ์ ๋ถ | ์์ | 100% | ์ ์ ์๋ |
| 2 | ์ง์ ๋ ์ธ ๋ถ๊ธฐ | ๋ ์ธ 0,2,4โฆ30 (16๊ฐ) | ๋ ์ธ 1,3,5โฆ31 (16๊ฐ) | 50% | 2๋ฐฐ ๋๋ฆผ |
| 3 | ํ์ ๋ ์ธ ๋ถ๊ธฐ | ๋ ์ธ 1,3,5โฆ31 (16๊ฐ) | ๋ ์ธ 0,2,4โฆ30 (16๊ฐ) | 50% | 2๋ฐฐ ๋๋ฆผ |
| 4 | ์๋ ด | 32๊ฐ ๋ ์ธ ์ ๋ถ | ์์ | 100% | ์ ์ ์๋ ๋ณต๊ท |
์์ ๋ถ์:
- 2๋จ๊ณ: ์ง์ ๋ ์ธ๋ง
compute_even()์ ์คํํ๊ณ ํ์ ๋ ์ธ์ ๋๊ธฐ - 3๋จ๊ณ: ํ์ ๋ ์ธ๋ง
compute_odd()๋ฅผ ์คํํ๊ณ ์ง์ ๋ ์ธ์ ๋๊ธฐ - ์ด ์์ ์๊ฐ:
time(compute_even) + time(compute_odd)(์์ฐจ ์คํ) - ๋ถ๊ธฐ ์๋ ๊ฒฝ์ฐ:
max(time(compute_even), time(compute_odd))(๋ณ๋ ฌ ์คํ)
์ฑ๋ฅ ์ํฅ:
- ๋ถ๊ธฐ: ์ํ๊ฐ ์คํ์ ๋ถ๋ฆฌ - ์ผ๋ถ ๋ ์ธ์ ํ์ฑ, ๋๋จธ์ง๋ ๋๊ธฐ
- ์์ฐจ ์คํ: ์๋ก ๋ค๋ฅธ ๊ฒฝ๋ก๊ฐ ๋ณ๋ ฌ์ด ์๋ ์์ฐจ์ ์ผ๋ก ์คํ
- ์๋ ด: ๋ชจ๋ ๋ ์ธ์ด ๋ค์ ํฉ๋ฅํ์ฌ ํจ๊ป ์งํ
- ๋น์ฉ: ๋ถ๊ธฐ๊ฐ ์๋ ์ํ๋ ํตํฉ ์คํ ๋๋น 2๋ฐฐ ์ด์์ ์๊ฐ ์์
์ํ ํจ์จ์ ์ํ ๋ชจ๋ฒ ์ฌ๋ก
์ํ ํจ์จ ํจํด
โ ์ฐ์: ๊ท ์ผ ์คํ (100% ํจ์จ)
# ๋ชจ๋ ๋ ์ธ์ด ๊ฐ์ ์์
์ํ - ๋ถ๊ธฐ ์์
var partial = a[global_i] * b[global_i]
var total = sum(partial)
์ฑ๋ฅ: 32๊ฐ ๋ ์ธ ๋ชจ๋ ๋์ ํ์ฑ
โ ๏ธ ํ์ฉ: ์์ธก ๊ฐ๋ฅํ ๋ถ๊ธฐ (~95% ํจ์จ)
# lane_id() ๊ธฐ๋ฐ ๋ถ๊ธฐ - ํ๋์จ์ด ์ต์ ํ๋จ
if lane_id() == 0:
output[block_idx] = sum(partial)
์ฑ๋ฅ: ๋จ์ผ ๋ ์ธ์ ์งง์ ์ฐ์ฐ, ์์ธก ๊ฐ๋ฅํ ํจํด
๐ถ ์ฃผ์: ๊ตฌ์กฐํ๋ ๋ถ๊ธฐ (~50-75% ํจ์จ)
# ๊ท์น์ ์ธ ํจํด์ ์ปดํ์ผ๋ฌ๊ฐ ์ต์ ํ ๊ฐ๋ฅ
if (global_i / 4) % 2 == 0:
result = method_a()
else:
result = method_b()
์ฑ๋ฅ: ์์ธก ๊ฐ๋ฅํ ๊ทธ๋ฃน, ์ผ๋ถ ์ต์ ํ ๊ฐ๋ฅ
โ ํํผ: ๋ฐ์ดํฐ ์์กด์ ๋ถ๊ธฐ (~25-50% ํจ์จ)
# ๋ฐ์ดํฐ์ ๋ฐ๋ผ ๋ ์ธ๋ง๋ค ๋ค๋ฅธ ๊ฒฝ๋ก๋ฅผ ํ ์ ์์
if input[global_i] > threshold: # ์์ธก ๋ถ๊ฐ๋ฅํ ๋ถ๊ธฐ
result = expensive_computation()
else:
result = simple_computation()
์ฑ๋ฅ: ๋ฌด์์ ๋ถ๊ธฐ๊ฐ ์ํ ํจ์จ์ ๋จ์ด๋จ๋ฆผ
๐ ์ต์ : ์ค์ฒฉ๋ ๋ฐ์ดํฐ ์์กด์ ๋ถ๊ธฐ (~10-25% ํจ์จ)
# ์์ธก ๋ถ๊ฐ๋ฅํ ๋ถ๊ธฐ์ ๋ค๋จ๊ณ ์ค์ฒฉ
if input[global_i] > threshold1:
if input[global_i] > threshold2:
result = very_expensive()
else:
result = expensive()
else:
result = simple()
์ฑ๋ฅ: ์ํ ํจ์จ์ด ์ฌ์ค์ ๋ฌด๋์ง
ํฌ๋ก์ค ์ํคํ ์ฒ ํธํ์ฑ
NVIDIA vs AMD ์ํ ํฌ๊ธฐ
from gpu.primitives.warp import WARP_SIZE
# NVIDIA GPUs: WARP_SIZE = 32
# AMD RDNA GPUs: WARP_SIZE = 32 (wavefront32 ๋ชจ๋)
# AMD CDNA GPUs: WARP_SIZE = 64 (์ ํต์ ์ธ wavefront64)
์ ์ค์ํ ๊น์:
- ๋ฉ๋ชจ๋ฆฌ ํจํด: ๋ณํฉ๋ ์ ๊ทผ์ด ์ํ ํฌ๊ธฐ์ ์์กด
- ์๊ณ ๋ฆฌ์ฆ ์ค๊ณ: ๋ฆฌ๋์ ํธ๋ฆฌ๊ฐ ์ํ ํฌ๊ธฐ๋ฅผ ๊ณ ๋ คํด์ผ ํจ
- ์ฑ๋ฅ ํ์ฅ: AMD์์ ์ํ๋น ๋ ์ธ์ด 2๋ฐฐ
์ด์ ๊ฐ๋ฅํ ์ํ ์ฝ๋ ์์ฑ
์ํคํ ์ฒ ์ ์ ์ ๋ต
โ
์ด์ ๊ฐ๋ฅ: ํญ์ WARP_SIZE ์ฌ์ฉ
comptime THREADS_PER_BLOCK = (WARP_SIZE, 1) # ์๋์ผ๋ก ์ ์
comptime ELEMENTS_PER_WARP = WARP_SIZE # ํ๋์จ์ด์ ๋ง๊ฒ ํ์ฅ
๊ฒฐ๊ณผ: NVIDIA/AMD (32)์ AMD (64) ๋ชจ๋์์ ์ต์ ์ผ๋ก ๋์
โ ์๋ชป๋ ๋ฐฉ์: ์ํ ํฌ๊ธฐ๋ฅผ ํ๋์ฝ๋ฉํ์ง ๋ง์ธ์
comptime THREADS_PER_BLOCK = (32, 1) # AMD GPU์์ ๋์ ์ ํจ!
comptime REDUCTION_SIZE = 32 # AMD์์ ์๋ชป๋ ๊ฐ!
๊ฒฐ๊ณผ: AMD์์ ์ฑ๋ฅ ์ ํ, ์ ํ์ฑ ๋ฌธ์ ๊ฐ๋ฅ
์ค์ ํ๋์จ์ด ์ํฅ
| GPU ์ํคํ ์ฒ | WARP_SIZE | ์ํ๋น ๋ฉ๋ชจ๋ฆฌ | ๋ฆฌ๋์ ๋จ๊ณ | ๋ ์ธ ํจํด |
|---|---|---|---|---|
| NVIDIA/AMD RDNA | 32 | 128 bytes (4ร32) | 5๋จ๊ณ: 32โ16โ8โ4โ2โ1 | ๋ ์ธ 0-31 |
| AMD CDNA | 64 | 256 bytes (4ร64) | 6๋จ๊ณ: 64โ32โ16โ8โ4โ2โ1 | ๋ ์ธ 0-63 |
64 vs 32์ ์ฑ๋ฅ ์ฐจ์ด:
- CDNA ์ฅ์ : ์ํ๋น 2๋ฐฐ์ ๋ฉ๋ชจ๋ฆฌ ๋์ญํญ
- CDNA ์ฅ์ : ์ํ๋น 2๋ฐฐ์ ์ฐ์ฐ๋
- NVIDIA/RDNA ์ฅ์ : ๋ธ๋ก๋น ๋ ๋ง์ ์ํ (๋ ๋์ ์ ์ ์จ)
- ์ฝ๋ ์ด์์ฑ: ๊ฐ์ ์์ค ์ฝ๋๋ก ์์ชฝ ๋ชจ๋ ์ต์ ์ฑ๋ฅ
์ํ์ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ํจํด
๋ณํฉ๋ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ํจํด
โ ์๋ฒฝ: ๋ณํฉ๋ ์ ๊ทผ (100% ๋์ญํญ ํ์ฉ)
# ์ธ์ ๋ ์ธ โ ์ธ์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์
var value = input[global_i] # ๋ ์ธ 0โinput[0], ๋ ์ธ 1โinput[1], ๋ฑ
๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ํจํด:
| ์ ๊ทผ ํจํด | NVIDIA/RDNA (32 ๋ ์ธ) | CDNA (64 ๋ ์ธ) | ๋์ญํญ ํ์ฉ | ์ฑ๋ฅ |
|---|---|---|---|---|
| โ ๋ณํฉ | ๋ ์ธ N โ ์ฃผ์ 4รN | ๋ ์ธ N โ ์ฃผ์ 4รN | 100% | ์ต์ |
| 1ํ ํธ๋์ญ์ : 128 bytes | 1ํ ํธ๋์ญ์ : 256 bytes | ์ ์ฒด ๋ฒ์ค ํญ | ๋น ๋ฆ | |
| โ ๋ถ์ฐ | ๋ ์ธ N โ ์์ ์ฃผ์ | ๋ ์ธ N โ ์์ ์ฃผ์ | ~6% | ์ต์ |
| 32ํ ๊ฐ๋ณ ํธ๋์ญ์ | 64ํ ๊ฐ๋ณ ํธ๋์ญ์ | ๋๋ถ๋ถ ์ ํด ๋ฒ์ค | 32๋ฐฐ ๋๋ฆผ |
์ฃผ์ ์์:
- ๋ณํฉ: ๋ ์ธ 0โ0, ๋ ์ธ 1โ4, ๋ ์ธ 2โ8, ๋ ์ธ 3โ12, โฆ
- ๋ถ์ฐ: ๋ ์ธ 0โ1000, ๋ ์ธ 1โ52, ๋ ์ธ 2โ997, ๋ ์ธ 3โ8, โฆ
๊ณต์ ๋ฉ๋ชจ๋ฆฌ ๋ฑ ํฌ ์ถฉ๋
๋ฑ ํฌ ์ถฉ๋์ด๋?
GPU ๊ณต์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋์ ์ ๊ทผ์ด ๊ฐ๋ฅํ 32๊ฐ์ ๋ ๋ฆฝ์ ์ธ ๋ฑ ํฌ๋ก ๋๋์ด ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค. ๋ฑ ํฌ ์ถฉ๋์ ์ํ ๋ด ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ฐ์ ๋ฑ ํฌ์ ์๋ก ๋ค๋ฅธ ์ฃผ์์ ๋์์ ์ ๊ทผํ๋ ค ํ ๋ ๋ฐ์ํฉ๋๋ค. ์ด ๊ฒฝ์ฐ ํ๋์จ์ด๊ฐ ์ ๊ทผ์ ์ง๋ ฌํํด์ผ ํ๋ฏ๋ก, ๋จ์ผ ์ฌ์ดํด์ด์ด์ผ ํ ์ฐ์ฐ์ด ์ฌ๋ฌ ์ฌ์ดํด๋ก ๋์ด๋ฉ๋๋ค.
ํต์ฌ ๊ฐ๋ :
- ์ถฉ๋ ์์: ๊ฐ ์ค๋ ๋๊ฐ ์๋ก ๋ค๋ฅธ ๋ฑ ํฌ์ ์ ๊ทผ โ ๋ชจ๋ ์ ๊ทผ์ด ๋์์ ๋ฐ์ (1 ์ฌ์ดํด)
- ๋ฑ ํฌ ์ถฉ๋: ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ฐ์ ๋ฑ ํฌ์ ์ ๊ทผ โ ์ ๊ทผ์ด ์์ฐจ์ ์ผ๋ก ๋ฐ์ (N๊ฐ ์ค๋ ๋์ N ์ฌ์ดํด)
- ๋ธ๋ก๋์บ์คํธ: ๋ชจ๋ ์ค๋ ๋๊ฐ ๊ฐ์ ์ฃผ์์ ์ ๊ทผ โ ํ๋์จ์ด๊ฐ 1 ์ฌ์ดํด๋ก ์ต์ ํ
๊ณต์ ๋ฉ๋ชจ๋ฆฌ ๋ฑ ํฌ ๊ตฌ์ฑ:
| ๋ฑ ํฌ | ์ฃผ์ (๋ฐ์ดํธ ์คํ์ ) | ์์ ๋ฐ์ดํฐ (float32) |
|---|---|---|
| ๋ฑ ํฌ 0 | 0, 128, 256, 384, โฆ | shared[0], shared[32], shared[64], โฆ |
| ๋ฑ ํฌ 1 | 4, 132, 260, 388, โฆ | shared[1], shared[33], shared[65], โฆ |
| ๋ฑ ํฌ 2 | 8, 136, 264, 392, โฆ | shared[2], shared[34], shared[66], โฆ |
| โฆ | โฆ | โฆ |
| ๋ฑ ํฌ 31 | 124, 252, 380, 508, โฆ | shared[31], shared[63], shared[95], โฆ |
๋ฑ ํฌ ์ถฉ๋ ์์:
| ์ ๊ทผ ํจํด | ๋ฑ ํฌ ์ฌ์ฉ | ์ฌ์ดํด | ์ฑ๋ฅ | ์ค๋ช |
|---|---|---|---|---|
| โ ์์ฐจ์ | shared[thread_idx.x] | 1 ์ฌ์ดํด | 100% | ๊ฐ ๋ ์ธ์ด ๋ค๋ฅธ ๋ฑ ํฌ ์ ๊ทผ |
| ๋ ์ธ 0โ๋ฑ ํฌ 0, ๋ ์ธ 1โ๋ฑ ํฌ 1, โฆ | ์ต์ | ์ถฉ๋ ์์ | ||
| โ ๋์ผ ์ธ๋ฑ์ค | shared[0] | 1 ์ฌ์ดํด | 100% | ๋ชจ๋ ๋ ์ธ์ด ๊ฐ์ ์ฃผ์์์ ๋ธ๋ก๋์บ์คํธ |
| 32๊ฐ ๋ ์ธ ์ ๋ถโ๋ฑ ํฌ 0 (๊ฐ์ ์ฃผ์) | ์ต์ | ์ถฉ๋ ์์ | ||
| โ ์คํธ๋ผ์ด๋ 2 | shared[thread_idx.x * 2] | 2 ์ฌ์ดํด | 50% | ๋ฑ ํฌ๋น 2๊ฐ ๋ ์ธ |
| ๋ ์ธ 0,16โ๋ฑ ํฌ 0; ๋ ์ธ 1,17โ๋ฑ ํฌ 1 | 2๋ฐฐ ๋๋ฆผ | ์ง๋ ฌํ๋ ์ ๊ทผ | ||
| ๐ ์คํธ๋ผ์ด๋ 32 | shared[thread_idx.x * 32] | 32 ์ฌ์ดํด | 3% | ๋ชจ๋ ๋ ์ธ์ด ๊ฐ์ ๋ฑ ํฌ ์ ๊ทผ |
| 32๊ฐ ๋ ์ธ ์ ๋ถโ๋ฑ ํฌ 0 (๋ค๋ฅธ ์ฃผ์) | 32๋ฐฐ ๋๋ฆผ | ์์ ํ ์ง๋ ฌํ |
์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ค์ ํ์ฉ
์ํ ์ฐ์ฐ์ด ๊ฐ์ฅ ํจ๊ณผ์ ์ธ ๊ฒฝ์ฐ
- ๋ฆฌ๋์
์ฐ์ฐ:
sum(),max()๋ฑ - ๋ธ๋ก๋์บ์คํธ ์ฐ์ฐ:
shuffle_idx()๋ก ๊ฐ ๊ณต์ - ์ด์ ํต์ :
shuffle_down()์ผ๋ก ์ฌ๋ผ์ด๋ฉ ์๋์ฐ - ๋์ ํฉ ์ฐ์ฐ:
prefix_sum()์ผ๋ก scan ์๊ณ ๋ฆฌ์ฆ
์ฑ๋ฅ ํน์ฑ
| ์ฐ์ฐ ์ ํ | ๊ธฐ์กด ๋ฐฉ์ | ์ํ ์ฐ์ฐ |
|---|---|---|
| ๋ฆฌ๋์ (32๊ฐ ์์) | ~20๊ฐ ๋ช ๋ น | 10๊ฐ ๋ช ๋ น |
| ๋ฉ๋ชจ๋ฆฌ ํธ๋ํฝ | ๋์ | ์ต์ |
| ๋๊ธฐํ ๋น์ฉ | ๋น์ฉ ๋์ | ๋ฌด๋ฃ |
| ์ฝ๋ ๋ณต์ก๋ | ๋์ | ๋ฎ์ |
๋ค์ ๋จ๊ณ
SIMT์ ๊ธฐ๋ฐ์ ์ดํดํ์ผ๋, ์ด ๊ฐ๋
์ด ์ด๋ป๊ฒ ๊ฐ๋ ฅํ ์ํ ์ฐ์ฐ์ ๊ฐ๋ฅํ๊ฒ ํ๋์ง ์์๋ณผ ์ฐจ๋ก์
๋๋ค. ๋ค์ ์น์
์์๋ sum()์ด ๋ณต์กํ ๋ฆฌ๋์
ํจํด์ ๊ฐ๋จํ๊ณ ํจ์จ์ ์ธ ํจ์ ํธ์ถ๋ก ์ด๋ป๊ฒ ๋ณํํ๋์ง ๋ณด์ฌ์ค๋๋ค.
โ ๋ค์: warp.sum()์ ํต์ฌ