Puzzle 6: 블둝

κ°œμš”

벑터 a의 각 μœ„μΉ˜μ— 10을 더해 output에 μ €μž₯ν•˜λŠ” 컀널을 κ΅¬ν˜„ν•΄ λ³΄μ„Έμš”.

μŠ€λ ˆλ“œ 블둝(thread block) (λ˜λŠ” μ€„μ—¬μ„œ 블둝)은 ν•˜λ‚˜μ˜ GPU λ©€ν‹°ν”„λ‘œμ„Έμ„œμ—μ„œ ν•¨κ»˜ μ‹€ν–‰λ˜λŠ” μŠ€λ ˆλ“œ λ¬ΆμŒμž…λ‹ˆλ‹€. 같은 블둝 μ•ˆμ˜ λͺ¨λ“  μŠ€λ ˆλ“œλŠ” 곡유 λ©”λͺ¨λ¦¬λ₯Ό ν•¨κ»˜ μ‚¬μš©ν•˜κ³  μ„œλ‘œ 동기화할 수 μžˆμŠ΅λ‹ˆλ‹€. 데이터가 ν•œ 블둝이 μ²˜λ¦¬ν•  수 μžˆλŠ” λ²”μœ„λ³΄λ‹€ 크면 GPUλŠ” μ—¬λŸ¬ 블둝을 μŠ€μΌ€μ€„λ§ν•˜κ³ , 각 블둝은 자기 λͺ«μ˜ 데이터λ₯Ό λ…λ¦½μ μœΌλ‘œ μ²˜λ¦¬ν•©λ‹ˆλ‹€. μŠ€λ ˆλ“œμ˜ μ „μ—­ μœ„μΉ˜λŠ” 블둝 λ‚΄ μœ„μΉ˜(thread_idx.x)와 μ†Œμ† 블둝(block_idx.x)을 합쳐 κ³„μ‚°ν•©λ‹ˆλ‹€: global_i = block_dim.x * block_idx.x + thread_idx.x.

μ°Έκ³ : 블둝당 μŠ€λ ˆλ“œ μˆ˜κ°€ a의 크기보닀 μž‘μŠ΅λ‹ˆλ‹€.

블둝 μ‹œκ°ν™” 블둝 μ‹œκ°ν™”

핡심 κ°œλ…

이 νΌμ¦μ—μ„œ λ‹€λ£¨λŠ” λ‚΄μš©:

  • μŠ€λ ˆλ“œ 블둝 크기보닀 큰 데이터 처리
  • μ—¬λŸ¬ λΈ”λ‘μ˜ μŠ€λ ˆλ“œ 쑰율
  • μ „μ—­ μŠ€λ ˆλ“œ μœ„μΉ˜ 계산

μ—¬κΈ°μ„œ 핡심은 μ—¬λŸ¬ μŠ€λ ˆλ“œ 블둝이 ν˜‘λ ₯ν•˜μ—¬ 단일 블둝 μš©λŸ‰λ³΄λ‹€ 큰 데이터λ₯Ό μ²˜λ¦¬ν•˜λ©΄μ„œλ„, μš”μ†Œμ™€ μŠ€λ ˆλ“œ κ°„ μ˜¬λ°”λ₯Έ 맀핑을 μœ μ§€ν•˜λŠ” 원리λ₯Ό μ΄ν•΄ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

μ™„μ„±ν•  μ½”λ“œ

comptime SIZE = 9
comptime BLOCKS_PER_GRID = (3, 1)
comptime THREADS_PER_BLOCK = (4, 1)
comptime dtype = DType.float32


def add_10_blocks(
    output: UnsafePointer[Scalar[dtype], MutAnyOrigin],
    a: UnsafePointer[Scalar[dtype], MutAnyOrigin],
    size: Int,
):
    var i = block_dim.x * block_idx.x + thread_idx.x
    # FILL ME IN (roughly 2 lines)


전체 μ½”λ“œ 보기: problems/p06/p06.mojo

μ°Έκ³ : 이 퍼즐의 TileTensor 버전은 거의 λ™μΌν•˜λ―€λ‘œ λ…μžμ—κ²Œ λ§‘κΉλ‹ˆλ‹€.

팁
  1. μ „μ—­ 인덱슀 계산: i = block_dim.x * block_idx.x + thread_idx.x
  2. κ°€λ“œ μΆ”κ°€: if i < size
  3. κ°€λ“œ λ‚΄λΆ€: output[i] = a[i] + 10.0

μ½”λ“œ μ‹€ν–‰

μ†”λ£¨μ…˜μ„ ν…ŒμŠ€νŠΈν•˜λ €λ©΄ ν„°λ―Έλ„μ—μ„œ λ‹€μŒ λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜μ„Έμš”:

pixi run p06
pixi run -e amd p06
pixi run -e apple p06
uv run poe p06

퍼즐을 아직 ν’€μ§€ μ•Šμ•˜λ‹€λ©΄ 좜λ ₯이 λ‹€μŒκ³Ό 같이 λ‚˜νƒ€λ‚©λ‹ˆλ‹€:

out: HostBuffer([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
expected: HostBuffer([10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0])

μ†”λ£¨μ…˜

def add_10_blocks(
    output: UnsafePointer[Scalar[dtype], MutAnyOrigin],
    a: UnsafePointer[Scalar[dtype], MutAnyOrigin],
    size: Int,
):
    var i = block_dim.x * block_idx.x + thread_idx.x
    if i < size:
        output[i] = a[i] + 10.0


이 μ†”λ£¨μ…˜μ€ 블둝 기반 GPU 처리의 핡심 κ°œλ…μ„ λ‹€λ£Ήλ‹ˆλ‹€:

  1. μ „μ—­ μŠ€λ ˆλ“œ 인덱싱

    • 블둝 μΈλ±μŠ€μ™€ μŠ€λ ˆλ“œ 인덱슀λ₯Ό κ²°ν•©: block_dim.x * block_idx.x + thread_idx.x

    • 각 μŠ€λ ˆλ“œλ₯Ό κ³ μœ ν•œ μ „μ—­ μœ„μΉ˜μ— λ§€ν•‘

    • 블둝당 3개 μŠ€λ ˆλ“œ μ˜ˆμ‹œ:

      Block 0: [0 1 2]
      Block 1: [3 4 5]
      Block 2: [6 7 8]
      
  2. 블둝 쑰율

    • 각 블둝은 μ—°μ†λœ 데이터 청크λ₯Ό 처리

    • 블둝 크기(3) < 데이터 크기(9)μ΄λ―€λ‘œ μ—¬λŸ¬ 블둝 ν•„μš”

    • 블둝 κ°„ μžλ™ μž‘μ—… λΆ„λ°°:

      Data:    [0 1 2 3 4 5 6 7 8]
      Block 0: [0 1 2]
      Block 1:       [3 4 5]
      Block 2:             [6 7 8]
      
  3. 경계 검사

    • κ°€λ“œ 쑰건 i < size둜 경계 μΌ€μ΄μŠ€ 처리
    • 데이터 크기가 블둝 크기둜 λ‚˜λˆ„μ–΄ λ–¨μ–΄μ§€μ§€ μ•Šμ„ λ•Œ λ²”μœ„λ₯Ό λ²—μ–΄λ‚œ μ ‘κ·Ό λ°©μ§€
    • 데이터 λλΆ€λΆ„μ˜ λΆˆμ™„μ „ν•œ 블둝 μ²˜λ¦¬μ— ν•„μˆ˜
  4. λ©”λͺ¨λ¦¬ μ ‘κ·Ό νŒ¨ν„΄

    • 병합(coalesced) λ©”λͺ¨λ¦¬ μ ‘κ·Ό: 블둝 λ‚΄ μŠ€λ ˆλ“œλ“€μ΄ μ—°μ†λœ λ©”λͺ¨λ¦¬μ— μ ‘κ·Ό
    • 각 μŠ€λ ˆλ“œκ°€ ν•˜λ‚˜μ˜ μš”μ†Œ 처리: output[i] = a[i] + 10.0
    • 블둝 μˆ˜μ€€ λ³‘λ ¬μ„±μœΌλ‘œ λ©”λͺ¨λ¦¬ λŒ€μ—­ν­μ„ 효율적으둜 ν™œμš©

이 νŒ¨ν„΄μ€ 단일 μŠ€λ ˆλ“œ 블둝 크기λ₯Ό μ΄ˆκ³Όν•˜λŠ” λŒ€κ·œλͺ¨ 데이터셋 처리의 κΈ°μ΄ˆκ°€ λ©λ‹ˆλ‹€.