Puzzle 11: ํ’€๋ง

๊ฐœ์š”

1D TileTensor a์—์„œ ๊ฐ ์œ„์น˜์˜ ์ง์ „ 3๊ฐœ ๊ฐ’์˜ ํ•ฉ์„ ๊ณ„์‚ฐํ•˜์—ฌ 1D TileTensor output์— ์ €์žฅํ•˜๋Š” ์ปค๋„์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.

ํ’€๋ง(pooling) ์€ ์ผ์ • ์˜์—ญ์˜ ๊ฐ’๋“ค์„ ํ•˜๋‚˜์˜ ์š”์•ฝ ๊ฐ’(์˜ˆ: ํ•ฉ, ์ตœ๋Œ“๊ฐ’, ํ‰๊ท )์œผ๋กœ ์••์ถ•ํ•˜๋Š” ์—ฐ์‚ฐ์ž…๋‹ˆ๋‹ค. ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ(sliding window) ๋Š” ์ž…๋ ฅ ์œ„๋กœ ๊ณ ์ • ํฌ๊ธฐ์˜ ์œˆ๋„์šฐ๋ฅผ ํ•œ ์นธ์”ฉ ์˜ฎ๊ฒจ ๊ฐ€๋ฉฐ ์ด ์••์ถ•์„ ๋ฐ˜๋ณต ์ ์šฉํ•ด, ์œˆ๋„์šฐ ์œ„์น˜๋งˆ๋‹ค ์ถœ๋ ฅ๊ฐ’์„ ํ•˜๋‚˜์”ฉ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์œˆ๋„์šฐ ํญ์ด 3์ด๊ณ  ์š”์•ฝ ํ•จ์ˆ˜๊ฐ€ ํ•ฉ์ด๋ฏ€๋กœ, ๊ฐ ์ถœ๋ ฅ ์›์†Œ๋Š” ํ˜„์žฌ ์›์†Œ์™€ ๊ทธ ์•ž ๋‘ ์›์†Œ์˜ ํ•ฉ์ด ๋ฉ๋‹ˆ๋‹ค(์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์›์†Œ๊ฐ€ 3๊ฐœ๋ณด๋‹ค ์ ์€ ๊ฒฝ๊ณ„ ์ง€์ ์—์„œ๋Š” ํŠน์ˆ˜ ์ผ€์ด์Šค๋กœ ์ฒ˜๋ฆฌ).

์ฐธ๊ณ : ๊ฐ ์œ„์น˜๋งˆ๋‹ค ์Šค๋ ˆ๋“œ 1๊ฐœ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ๋‹น ์ „์—ญ ์ฝ๊ธฐ 1ํšŒ, ์ „์—ญ ์“ฐ๊ธฐ 1ํšŒ๋งŒ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Pooling ์‹œ๊ฐํ™” Pooling ์‹œ๊ฐํ™”

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

์ด ํผ์ฆ์—์„œ ๋ฐฐ์šธ ๋‚ด์šฉ:

  • TileTensor๋กœ ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ ์—ฐ์‚ฐ ๊ตฌํ˜„ํ•˜๊ธฐ
  • Puzzle 8์—์„œ ๋‹ค๋ฃฌ TileTensor ์ฃผ์†Œ ๊ณต๊ฐ„(address_space)์œผ๋กœ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌํ•˜๊ธฐ
  • ํšจ์œจ์ ์ธ ์ด์›ƒ ์ ‘๊ทผ ํŒจํ„ด
  • ๊ฒฝ๊ณ„ ์กฐ๊ฑด ์ฒ˜๋ฆฌ

ํ•ต์‹ฌ์€ TileTensor๊ฐ€ ํšจ์œจ์ ์ธ ์œˆ๋„์šฐ ๊ธฐ๋ฐ˜ ์—ฐ์‚ฐ์€ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ๊ฐ„์†Œํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

๊ตฌ์„ฑ

  • ๋ฐฐ์—ด ํฌ๊ธฐ: SIZE = 8
  • ๋ธ”๋ก๋‹น ์Šค๋ ˆ๋“œ ์ˆ˜: TPB = 8
  • ์œˆ๋„์šฐ ํฌ๊ธฐ: 3
  • ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ: TPB๊ฐœ

์ฐธ๊ณ :

  • TileTensor ํ• ๋‹น: stack_allocation[dtype=dtype, address_space=AddressSpace.SHARED](row_major[TPB]()) ์‚ฌ์šฉ
  • ์œˆ๋„์šฐ ์ ‘๊ทผ: 3๊ฐœ์งœ๋ฆฌ ์œˆ๋„์šฐ์— ์ž์—ฐ์Šค๋Ÿฌ์šด ์ธ๋ฑ์‹ฑ
  • ๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ: ์ฒ˜์Œ ๋‘ ์œ„์น˜๋Š” ํŠน์ˆ˜ ์ผ€์ด์Šค
  • ๋ฉ”๋ชจ๋ฆฌ ํŒจํ„ด: ์Šค๋ ˆ๋“œ๋‹น ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๋กœ๋“œ 1ํšŒ

์™„์„ฑํ•  ์ฝ”๋“œ

comptime TPB = 8
comptime SIZE = 8
comptime BLOCKS_PER_GRID = (1, 1)
comptime THREADS_PER_BLOCK = (TPB, 1)
comptime dtype = DType.float32
comptime layout = row_major[SIZE]()
comptime LayoutType = type_of(layout)


def pooling(
    output: TileTensor[mut=True, dtype, LayoutType, MutAnyOrigin],
    a: TileTensor[mut=False, dtype, LayoutType, ImmutAnyOrigin],
    size: Int,
):
    # Allocate shared memory using stack_allocation
    var shared = stack_allocation[
        dtype=dtype, address_space=AddressSpace.SHARED
    ](row_major[TPB]())

    var global_i = block_dim.x * block_idx.x + thread_idx.x
    var local_i = thread_idx.x
    # FILL ME IN (roughly 10 lines)


์ „์ฒด ํŒŒ์ผ ๋ณด๊ธฐ: problems/p11/p11.mojo

ํŒ
  1. TileTensor์™€ ์ฃผ์†Œ ๊ณต๊ฐ„(address_space)์œผ๋กœ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ์ƒ์„ฑ
  2. ์ž์—ฐ์Šค๋Ÿฌ์šด ์ธ๋ฑ์‹ฑ์œผ๋กœ ๋ฐ์ดํ„ฐ ๋กœ๋“œ: shared[local_i] = a[global_i]
  3. ์ฒ˜์Œ ๋‘ ์œ„์น˜๋ฅผ ํŠน์ˆ˜ ์ผ€์ด์Šค๋กœ ์ฒ˜๋ฆฌ
  4. ์œˆ๋„์šฐ ์—ฐ์‚ฐ์— ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ํ™œ์šฉ
  5. ๊ฒฝ๊ณ„ ์ดˆ๊ณผ ์ ‘๊ทผ์— ๊ฐ€๋“œ ์ถ”๊ฐ€

์ฝ”๋“œ ์‹คํ–‰

์†”๋ฃจ์…˜์„ ํ…Œ์ŠคํŠธํ•˜๋ ค๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์„ธ์š”:

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

ํผ์ฆ์„ ์•„์ง ํ’€์ง€ ์•Š์•˜๋‹ค๋ฉด ์ถœ๋ ฅ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

out: HostBuffer([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
expected: HostBuffer([0.0, 1.0, 3.0, 6.0, 9.0, 12.0, 15.0, 18.0])

์†”๋ฃจ์…˜

def pooling(
    output: TileTensor[mut=True, dtype, LayoutType, MutAnyOrigin],
    a: TileTensor[mut=False, dtype, LayoutType, ImmutAnyOrigin],
    size: Int,
):
    # Allocate shared memory using stack_allocation
    var shared = stack_allocation[
        dtype=dtype, address_space=AddressSpace.SHARED
    ](row_major[TPB]())

    var global_i = block_dim.x * block_idx.x + thread_idx.x
    var local_i = thread_idx.x

    # Load data into shared memory
    if global_i < size:
        shared[local_i] = a[global_i]

    # Synchronize threads within block
    barrier()

    # Handle first two special cases
    if global_i == 0:
        output[0] = shared[0]
    elif global_i == 1:
        output[1] = shared[0] + shared[1]
    # Handle general case
    elif 1 < global_i < size:
        output[global_i] = (
            shared[local_i - 2] + shared[local_i - 1] + shared[local_i]
        )


TileTensor๋ฅผ ํ™œ์šฉํ•œ ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ ํ•ฉ๊ณ„ ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค. ์ฃผ์š” ๋‹จ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ์„ค์ •

    • TileTensor๊ฐ€ ์ฃผ์†Œ ๊ณต๊ฐ„(address_space)์œผ๋กœ ๋ธ”๋ก ๋กœ์ปฌ ์ €์žฅ์†Œ๋ฅผ ์ƒ์„ฑ:

      shared = stack_allocation[dtype=dtype, address_space=AddressSpace.SHARED](row_major[TPB]())
      
    • ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ํ•˜๋‚˜์”ฉ ๋กœ๋“œ:

      Input array:  [0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0]
      Block shared: [0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0]
      
    • barrier()๋กœ ๋ชจ๋“  ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์™„๋ฃŒ๋ฅผ ๋ณด์žฅ

  2. ๊ฒฝ๊ณ„ ์ผ€์ด์Šค

    • ์œ„์น˜ 0: ํ•˜๋‚˜๋งŒ

      output[0] = shared[0] = 0.0
      
    • ์œ„์น˜ 1: ์ฒ˜์Œ ๋‘ ๊ฐ’์˜ ํ•ฉ

      output[1] = shared[0] + shared[1] = 0.0 + 1.0 = 1.0
      
  3. ๋ฉ”์ธ ์œˆ๋„์šฐ ์—ฐ์‚ฐ

    • ์œ„์น˜ 2 ์ดํ›„:

      Position 2: shared[0] + shared[1] + shared[2] = 0.0 + 1.0 + 2.0 = 3.0
      Position 3: shared[1] + shared[2] + shared[3] = 1.0 + 2.0 + 3.0 = 6.0
      Position 4: shared[2] + shared[3] + shared[4] = 2.0 + 3.0 + 4.0 = 9.0
      ...
      
    • TileTensor์˜ ์ž์—ฐ์Šค๋Ÿฌ์šด ์ธ๋ฑ์‹ฑ:

      # 3๊ฐœ์งœ๋ฆฌ ์Šฌ๋ผ์ด๋”ฉ ์œˆ๋„์šฐ
      window_sum = shared[i-2] + shared[i-1] + shared[i]
      

๋‹จ์ผ ๋ธ”๋ก ์ „์ œ: ์ด ํผ์ฆ์ด BLOCKS_PER_GRID = (1, 1)๊ณผ SIZE == TPB = 8๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์–ด์„œ ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ๋ธ”๋ก์— ์†ํ•˜๊ณ  global_i == local_i๊ฐ€ ๋ณด์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ์†”๋ฃจ์…˜์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ œ์•ฝ์—์„œ๋Š” global_i > 1์ผ ๋•Œ๋งˆ๋‹ค local_i >= 2์ด๋ฏ€๋กœ shared[local_i - 2]์™€ shared[local_i - 1]์ด ์–ธ์ œ๋‚˜ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์ค‘ ๋ธ”๋ก ์ปค๋„์—์„œ๋Š” 0๋ฒˆ ๋ธ”๋ก ์ดํ›„์˜ ๊ฐ ๋ธ”๋ก์—์„œ ์ฒซ ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ global_i > 1์ธ๋ฐ๋„ local_i = 0 ๋˜๋Š” local_i = 1์ด ๋˜์–ด ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ ๋ฒ”์œ„ ์ดˆ๊ณผ ์ฝ๊ธฐ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์ค‘ ๋ธ”๋ก ํ’€๋ง์—์„œ ์•ˆ์ •์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ํŒจํ„ด์€ local_i๋กœ ๊ฐ€๋“œ๋ฅผ ๊ฑธ๊ณ , ํ—ค์ผ๋กœ(halo) ์›์†Œ์— ๋Œ€ํ•ด์„œ๋Š” ์ „์—ญ ์ฝ๊ธฐ๋กœ ๋Œ€์ฒดํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค:

if local_i >= 2:
    output[global_i] = shared[local_i-2] + shared[local_i-1] + shared[local_i]
elif local_i == 1 and global_i >= 2:
    output[global_i] = a[global_i-2] + shared[0] + shared[1]
elif local_i == 0 and global_i >= 2:
    output[global_i] = a[global_i-2] + a[global_i-1] + shared[0]
  1. ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ํŒจํ„ด
    • ์Šค๋ ˆ๋“œ๋งˆ๋‹ค ๊ณต์œ  ํ…์„œ๋กœ ์ „์—ญ ์ฝ๊ธฐ 1ํšŒ
    • ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ†ตํ•œ ํšจ์œจ์ ์ธ ์ด์›ƒ ์ ‘๊ทผ
    • TileTensor์˜ ์žฅ์ :
      • ์ž๋™ ๊ฒฝ๊ณ„ ๊ฒ€์‚ฌ
      • ์ž์—ฐ์Šค๋Ÿฌ์šด ์œˆ๋„์šฐ ์ธ๋ฑ์‹ฑ
      • ๋ ˆ์ด์•„์›ƒ์„ ์ธ์‹ํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ
      • ์ „ ๊ณผ์ •์— ๊ฑธ์นœ ํƒ€์ž… ์•ˆ์ „์„ฑ

๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ์˜ ์„ฑ๋Šฅ๊ณผ TileTensor์˜ ์•ˆ์ „์„ฑ ๋ฐ ํŽธ์˜์„ฑ์„ ๊ฒฐํ•ฉํ•œ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค:

  • ์ „์—ญ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ์ตœ์†Œํ™”
  • ์œˆ๋„์šฐ ์—ฐ์‚ฐ ๊ฐ„์†Œํ™”
  • ๊น”๋”ํ•œ ๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ
  • ๋ณ‘ํ•ฉ ์ ‘๊ทผ ํŒจํ„ด ์œ ์ง€

์ตœ์ข… ์ถœ๋ ฅ์€ ๋ˆ„์  ์œˆ๋„์šฐ ํ•ฉ๊ณ„์ž…๋‹ˆ๋‹ค:

[0.0, 1.0, 3.0, 6.0, 9.0, 12.0, 15.0, 18.0]