Puzzle 25: ์›Œํ”„ ํ†ต์‹ 

๊ฐœ์š”

Puzzle 25: ์›Œํ”„ ํ†ต์‹  ๊ธฐ๋ณธ ์š”์†Œ์—์„œ๋Š” ๊ณ ๊ธ‰ GPU ์›Œํ”„ ๋ ˆ๋ฒจ ํ†ต์‹  ์—ฐ์‚ฐ - ์›Œํ”„ ๋‚ด์—์„œ ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ๊ตํ™˜๊ณผ ์กฐ์ • ํŒจํ„ด์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ํ•˜๋“œ์›จ์–ด ๊ฐ€์† ๊ธฐ๋ณธ ์š”์†Œ๋ฅผ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค. shuffle_down๊ณผ broadcast๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณต์žกํ•œ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ํŒจํ„ด ์—†์ด ์ด์›ƒ ํ†ต์‹ ๊ณผ ์ง‘ํ•ฉ ์กฐ์ •์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›๋‹ˆ๋‹ค.

Part VII: GPU ์›Œํ”„ ํ†ต์‹ ์—์„œ๋Š” ์Šค๋ ˆ๋“œ ๊ทธ๋ฃน ๋‚ด ์›Œํ”„ ๋ ˆ๋ฒจ ๋ฐ์ดํ„ฐ ์ด๋™ ์—ฐ์‚ฐ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ + ์ธ๋ฑ์‹ฑ + ๊ฒฝ๊ณ„ ๊ฒ€์‚ฌ ํŒจํ„ด์„ ํ•˜๋“œ์›จ์–ด ์ตœ์ ํ™”๋œ ๋ฐ์ดํ„ฐ ์ด๋™์„ ํ™œ์šฉํ•˜๋Š” ํšจ์œจ์ ์ธ ์›Œํ”„ ํ†ต์‹  ํ˜ธ์ถœ๋กœ ๋Œ€์ฒดํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ํ†ต์ฐฐ: GPU ์›Œํ”„๋Š” ๋ก์Šคํ…์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค - Mojo์˜ ์›Œํ”„ ํ†ต์‹  ์—ฐ์‚ฐ์€ ์ด ๋™๊ธฐํ™”๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ž๋™ ๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ์™€ ๋ช…์‹œ์  ๋™๊ธฐํ™” ์—†์ด ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ๊ตํ™˜ ๊ธฐ๋ณธ ์š”์†Œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋ฐฐ์šธ ๋‚ด์šฉ

์›Œํ”„ ํ†ต์‹  ๋ชจ๋ธ

GPU ์›Œํ”„ ๋‚ด ๊ธฐ๋ณธ ํ†ต์‹  ํŒจํ„ด์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค:

GPU ์›Œํ”„ (32 ์Šค๋ ˆ๋“œ, SIMT ๋ก์Šคํ… ์‹คํ–‰)
โ”œโ”€โ”€ ๋ ˆ์ธ 0  โ”€โ”€shuffle_downโ”€โ”€> ๋ ˆ์ธ 1  โ”€โ”€shuffle_downโ”€โ”€> ๋ ˆ์ธ 2
โ”œโ”€โ”€ ๋ ˆ์ธ 1  โ”€โ”€shuffle_downโ”€โ”€> ๋ ˆ์ธ 2  โ”€โ”€shuffle_downโ”€โ”€> ๋ ˆ์ธ 3
โ”œโ”€โ”€ ๋ ˆ์ธ 2  โ”€โ”€shuffle_downโ”€โ”€> ๋ ˆ์ธ 3  โ”€โ”€shuffle_downโ”€โ”€> ๋ ˆ์ธ 4
โ”‚   ...
โ””โ”€โ”€ ๋ ˆ์ธ 31 โ”€โ”€shuffle_downโ”€โ”€> undefined (๊ฒฝ๊ณ„)

๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ํŒจํ„ด:
๋ ˆ์ธ 0 โ”€โ”€broadcastโ”€โ”€> ๋ชจ๋“  ๋ ˆ์ธ (0, 1, 2, ..., 31)

ํ•˜๋“œ์›จ์–ด ํ˜„์‹ค:

  • ๋ ˆ์ง€์Šคํ„ฐ ๊ฐ„ ์ง์ ‘ ํ†ต์‹ : ๋ฐ์ดํ„ฐ๊ฐ€ ์Šค๋ ˆ๋“œ ๋ ˆ์ง€์Šคํ„ฐ ์‚ฌ์ด๋ฅผ ์ง์ ‘ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค
  • ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ ์ œ๋กœ: ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
  • ์ž๋™ ๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ: ํ•˜๋“œ์›จ์–ด๊ฐ€ ์›Œํ”„ ๊ฒฝ๊ณ„์˜ ์˜ˆ์™ธ ์ƒํ™ฉ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค
  • ๋‹จ์ผ ์‚ฌ์ดํด ์—ฐ์‚ฐ: ํ•˜๋‚˜์˜ ๋ช…๋ น ์‚ฌ์ดํด์—์„œ ํ†ต์‹ ์ด ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค

Mojo์˜ ์›Œํ”„ ํ†ต์‹  ์—ฐ์‚ฐ

gpu.primitives.warp์˜ ํ•ต์‹ฌ ํ†ต์‹  ๊ธฐ๋ณธ ์š”์†Œ๋ฅผ ๋ฐฐ์›๋‹ˆ๋‹ค:

  1. shuffle_down(value, offset): ๋” ๋†’์€ ์ธ๋ฑ์Šค์˜ ๋ ˆ์ธ์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ธฐ (์ด์›ƒ ์ ‘๊ทผ)
  2. broadcast(value): ๋ ˆ์ธ 0์˜ ๊ฐ’์„ ๋ชจ๋“  ๋ ˆ์ธ์— ๊ณต์œ  (์ผ๋Œ€๋‹ค)
  3. shuffle_idx(value, lane): ํŠน์ • ๋ ˆ์ธ์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ธฐ (์ž„์˜ ์ ‘๊ทผ)
  4. shuffle_up(value, offset): ๋” ๋‚ฎ์€ ์ธ๋ฑ์Šค์˜ ๋ ˆ์ธ์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ธฐ (์—ญ๋ฐฉํ–ฅ ์ด์›ƒ)

์ฐธ๊ณ : ์ด ํผ์ฆ์€ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ํ†ต์‹  ํŒจํ„ด์ธ shuffle_down()๊ณผ broadcast()์— ์ดˆ์ ์„ ๋งž์ถฅ๋‹ˆ๋‹ค. ๋ชจ๋“  ์›Œํ”„ ์—ฐ์‚ฐ์— ๋Œ€ํ•œ ์ „์ฒด ๋‚ด์šฉ์€ Mojo GPU ์›Œํ”„ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

์„ฑ๋Šฅ ๋ณ€ํ™˜ ์˜ˆ์‹œ

# ๋ณต์žกํ•œ ์ด์›ƒ ์ ‘๊ทผ ํŒจํ„ด (๊ธฐ์กด ๋ฐฉ์‹):
shared = LayoutTensor[
    dtype,
    Layout.row_major(WARP_SIZE),
    MutAnyOrigin,
    address_space = AddressSpace.SHARED,
].stack_allocation()
shared[local_i] = input[global_i]
barrier()
if local_i < WARP_SIZE - 1:
    next_value = shared[local_i + 1]  # ์ด์›ƒ ์ ‘๊ทผ
    result = next_value - shared[local_i]
else:
    result = 0  # ๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ
barrier()

# ์›Œํ”„ ํ†ต์‹ ์€ ์ด ๋ชจ๋“  ๋ณต์žก์„ฑ์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค:
current_val = input[global_i]
next_val = shuffle_down(current_val, 1)  # ์ด์›ƒ์— ์ง์ ‘ ์ ‘๊ทผ
if lane < WARP_SIZE - 1:
    result = next_val - current_val
else:
    result = 0

์›Œํ”„ ํ†ต์‹ ์ด ๋น›๋‚˜๋Š” ์ˆœ๊ฐ„

์„ฑ๋Šฅ ํŠน์„ฑ์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค:

ํ†ต์‹  ํŒจํ„ด๊ธฐ์กด ๋ฐฉ์‹์›Œํ”„ ์—ฐ์‚ฐ
์ด์›ƒ ์ ‘๊ทผ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ๋ ˆ์ง€์Šคํ„ฐ ๊ฐ„ ์ง์ ‘ ํ†ต์‹ 
์Šคํ…์‹ค ์—ฐ์‚ฐ๋ณต์žกํ•œ ์ธ๋ฑ์‹ฑ๊ฐ„๋‹จํ•œ ์…”ํ”Œ ํŒจํ„ด
๋ธ”๋ก ์กฐ์ •๋ฐฐ๋ฆฌ์–ด + ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ๋‹จ์ผ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ
๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ์ˆ˜๋™ ๊ฒ€์‚ฌํ•˜๋“œ์›จ์–ด ์ž๋™ ์ฒ˜๋ฆฌ

์„ ์ˆ˜ ์ง€์‹

์›Œํ”„ ํ†ต์‹ ์— ๋“ค์–ด๊ฐ€๊ธฐ ์ „์— ๋‹ค์Œ ๋‚ด์šฉ์— ์ต์ˆ™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

  • Part VII ์›Œํ”„ ๊ธฐ์ดˆ: SIMT ์‹คํ–‰๊ณผ ๊ธฐ๋ณธ ์›Œํ”„ ์—ฐ์‚ฐ์— ๋Œ€ํ•œ ์ดํ•ด (Puzzle 24: ์›Œํ”„ ๊ธฐ์ดˆ ์ฐธ๊ณ )
  • GPU ์Šค๋ ˆ๋“œ ๊ณ„์ธต ๊ตฌ์กฐ: ๋ธ”๋ก, ์›Œํ”„, ๋ ˆ์ธ ๋ฒˆํ˜ธ ๋งค๊ธฐ๊ธฐ
  • LayoutTensor ์—ฐ์‚ฐ: ๋กœ๋“œ, ์ €์žฅ, ํ…์„œ ์กฐ์ž‘
  • ๊ฒฝ๊ณ„ ์กฐ๊ฑด ์ฒ˜๋ฆฌ: ๋ณ‘๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ฐ€์žฅ์ž๋ฆฌ ์ผ€์ด์Šค ๊ด€๋ฆฌ

ํ•™์Šต ๊ฒฝ๋กœ

1. shuffle_down์„ ์ด์šฉํ•œ ์ด์›ƒ ํ†ต์‹ 

โ†’ warp.shuffle_down()

์Šคํ…์‹ค ์—ฐ์‚ฐ๊ณผ ์œ ํ•œ ์ฐจ๋ถ„์„ ์œ„ํ•œ ์ด์›ƒ ๊ธฐ๋ฐ˜ ํ†ต์‹  ํŒจํ„ด์„ ๋ฐฐ์›๋‹ˆ๋‹ค.

๋ฐฐ์šธ ๋‚ด์šฉ:

  • shuffle_down()์œผ๋กœ ์ธ์ ‘ ๋ ˆ์ธ ๋ฐ์ดํ„ฐ ์ ‘๊ทผํ•˜๊ธฐ
  • ์œ ํ•œ ์ฐจ๋ถ„๊ณผ ์ด๋™ ํ‰๊ท  ๊ตฌํ˜„
  • ์›Œํ”„ ๊ฒฝ๊ณ„ ์ž๋™ ์ฒ˜๋ฆฌ
  • ํ™•์žฅ๋œ ์ด์›ƒ ์ ‘๊ทผ์„ ์œ„ํ•œ ๋‹ค์ค‘ ์˜คํ”„์…‹ ์…”ํ”Œ

ํ•ต์‹ฌ ํŒจํ„ด:

current_val = input[global_i]
next_val = shuffle_down(current_val, 1)
if lane < WARP_SIZE - 1:
    result = compute_with_neighbors(current_val, next_val)

2. ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ๋ฅผ ์ด์šฉํ•œ ์ง‘ํ•ฉ ์กฐ์ •

โ†’ warp.broadcast()

๋ธ”๋ก ๋ ˆ๋ฒจ ์กฐ์ •๊ณผ ์ง‘ํ•ฉ์  ์˜์‚ฌ๊ฒฐ์ •์„ ์œ„ํ•œ ์ผ๋Œ€๋‹ค ํ†ต์‹  ํŒจํ„ด์„ ๋ฐฐ์›๋‹ˆ๋‹ค.

๋ฐฐ์šธ ๋‚ด์šฉ:

  • broadcast()๋กœ ๊ณ„์‚ฐ๋œ ๊ฐ’์„ ๋ชจ๋“  ๋ ˆ์ธ์— ๊ณต์œ 
  • ๋ธ”๋ก ๋ ˆ๋ฒจ ํ†ต๊ณ„์™€ ์ง‘ํ•ฉ์  ์˜์‚ฌ๊ฒฐ์ • ๊ตฌํ˜„
  • ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ์™€ ์กฐ๊ฑด๋ถ€ ๋กœ์ง ๊ฒฐํ•ฉ
  • ๊ณ ๊ธ‰ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ-์…”ํ”Œ ์กฐ์ • ํŒจํ„ด

ํ•ต์‹ฌ ํŒจํ„ด:

var shared_value = 0.0
if lane == 0:
    shared_value = compute_block_statistic()
shared_value = broadcast(shared_value)
result = use_shared_value(shared_value, local_data)

ํ•ต์‹ฌ ๊ฐœ๋…

ํ†ต์‹  ํŒจํ„ด

์›Œํ”„ ํ†ต์‹ ์˜ ๊ธฐ๋ณธ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค:

  • ์ด์›ƒ ํ†ต์‹ : ๋ ˆ์ธ ๊ฐ„ ์ธ์ ‘ ๋ฐ์ดํ„ฐ ๊ตํ™˜
  • ์ง‘ํ•ฉ ์กฐ์ •: ํ•˜๋‚˜์˜ ๋ ˆ์ธ์—์„œ ๋ชจ๋“  ๋ ˆ์ธ์œผ๋กœ ์ •๋ณด ๊ณต์œ 
  • ์Šคํ…์‹ค ์—ฐ์‚ฐ: ๊ณ ์ •๋œ ํŒจํ„ด์œผ๋กœ ์ด์›ƒ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ
  • ๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ: ์›Œํ”„ ๊ฐ€์žฅ์ž๋ฆฌ์—์„œ์˜ ํ†ต์‹  ๊ด€๋ฆฌ

ํ•˜๋“œ์›จ์–ด ์ตœ์ ํ™”

์›Œํ”„ ํ†ต์‹ ์ด GPU ํ•˜๋“œ์›จ์–ด์— ๋งคํ•‘๋˜๋Š” ๋ฐฉ์‹์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค:

  • ๋ ˆ์ง€์Šคํ„ฐ ํŒŒ์ผ ํ†ต์‹ : ์Šค๋ ˆ๋“œ ๊ฐ„ ๋ ˆ์ง€์Šคํ„ฐ ์ง์ ‘ ์ ‘๊ทผ
  • SIMT ์‹คํ–‰: ๋ชจ๋“  ๋ ˆ์ธ์ด ํ†ต์‹ ์„ ๋™์‹œ์— ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค
  • ์ œ๋กœ ์ง€์—ฐ ์‹œ๊ฐ„: ์‹คํ–‰ ์œ ๋‹› ๋‚ด์—์„œ ํ†ต์‹ ์ด ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค
  • ์ž๋™ ๋™๊ธฐํ™”: ๋ช…์‹œ์  ๋ฐฐ๋ฆฌ์–ด๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค

์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋ณ€ํ™˜

๊ธฐ์กด ๋ณ‘๋ ฌ ํŒจํ„ด์„ ์›Œํ”„ ํ†ต์‹ ์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค:

  • ๋ฐฐ์—ด ์ด์›ƒ ์ ‘๊ทผ โ†’ shuffle_down()
  • ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ์กฐ์ • โ†’ broadcast()
  • ๋ณต์žกํ•œ ๊ฒฝ๊ณ„ ๋กœ์ง โ†’ ํ•˜๋“œ์›จ์–ด ์ž๋™ ์ฒ˜๋ฆฌ
  • ๋‹ค๋‹จ๊ณ„ ๋™๊ธฐํ™” โ†’ ๋‹จ์ผ ํ†ต์‹  ์—ฐ์‚ฐ

์‹œ์ž‘ํ•˜๊ธฐ

์ด์›ƒ ๊ธฐ๋ฐ˜ ์…”ํ”Œ ์—ฐ์‚ฐ์œผ๋กœ ๊ธฐ์ดˆ๋ฅผ ๋‹ค์ง„ ๋‹ค์Œ, ๊ณ ๊ธ‰ ์กฐ์ •์„ ์œ„ํ•œ ์ง‘ํ•ฉ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ํŒจํ„ด์œผ๋กœ ๋‚˜์•„๊ฐ‘๋‹ˆ๋‹ค.

๐Ÿ’ก ์„ฑ๊ณต ํŒ: ์›Œํ”„ ํ†ต์‹ ์„ ๊ฐ™์€ ์›Œํ”„ ๋‚ด ์Šค๋ ˆ๋“œ ๊ฐ„์˜ ํ•˜๋“œ์›จ์–ด ๊ฐ€์† ๋ฉ”์‹œ์ง€ ํŒจ์‹ฑ์œผ๋กœ ์ƒ๊ฐํ•˜์„ธ์š”. ์ด ๋ฉ˜ํƒˆ ๋ชจ๋ธ์ด GPU์˜ SIMT ์•„ํ‚คํ…์ฒ˜๋ฅผ ํ™œ์šฉํ•˜๋Š” ํšจ์œจ์ ์ธ ํ†ต์‹  ํŒจํ„ด์œผ๋กœ ์•ˆ๋‚ดํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•™์Šต ๋ชฉํ‘œ: Puzzle 25๋ฅผ ๋งˆ์น˜๋ฉด, ์›Œํ”„ ํ†ต์‹ ์ด ๋ณต์žกํ•œ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ํŒจํ„ด์„ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์„ ์ธ์‹ํ•˜์—ฌ ๋” ๊ฐ„๋‹จํ•˜๊ณ  ๋น ๋ฅธ ์ด์›ƒ ๊ธฐ๋ฐ˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ ์กฐ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ: warp.shuffle_down() ์—์„œ ์ด์›ƒ ํ†ต์‹ ์„ ๋ฐฐ์šด ๋‹ค์Œ, warp.broadcast() ์—์„œ ์ง‘ํ•ฉ ์กฐ์ • ํŒจํ„ด์œผ๋กœ ๋‚˜์•„๊ฐ€์„ธ์š”.