tiled matmul이 중요한 이유

naive matrix multiply를 이해했다면 다음 단계는 거의 항상 tiled matrix multiply다. 이 예제가 중요한 이유는 shared memory, block 협업, 재사용, memory traffic 감소라는 GPU 핵심 개념이 한 번에 드러나기 때문이다.

tile이라는 발상

전체 행렬을 한 번에 다루는 대신, 작은 정사각형 혹은 직사각형 조각으로 나누어 처리하는 것이 tile 방식이다. block 하나가 출력 행렬의 tile 하나를 맡고, 그 tile을 계산하는 데 필요한 입력 행렬의 일부를 shared memory로 가져온다.

이 구조의 핵심은 같은 입력 조각을 block 내부 여러 thread가 재사용할 수 있다는 점이다.

왜 shared memory가 효과적인가

예를 들어 출력 tile을 계산하려면 A의 일부 row 조각과 B의 일부 column 조각이 반복해서 필요하다. naive kernel에서는 각 thread가 이를 global memory에서 따로 읽는다. 반면 tiled kernel에서는:

  1. block 내부 thread가 협업해서 입력 tile을 load한다
  2. shared memory에 올린다
  3. 각 thread가 그 tile을 반복 사용한다

이렇게 하면 global memory 접근 횟수가 크게 줄어든다.

block과 tile 크기의 관계

tiled matmul에서는 block 크기와 tile 크기가 거의 설계의 중심이다. 너무 작은 tile은 재사용 효과가 약하고, 너무 큰 tile은 shared memory와 register를 많이 먹어 occupancy를 깎을 수 있다.

즉, 여기서도 정답 하나보다 균형이 중요하다.

  • tile이 크면 재사용은 좋아질 수 있다
  • 하지만 shared memory footprint가 커진다
  • register usage도 함께 늘어날 수 있다

이 때문에 타일 크기는 하드웨어 자원과 함께 봐야 한다.

synchronization이 왜 필요한가

block 내부 thread들이 shared memory를 함께 쓰기 때문에, 입력 tile을 다 올리기 전에 어떤 thread가 계산을 시작하면 안 된다. 그래서 보통 tile load 후 synchronization이 들어간다.

이 barrier는 필수지만, 동시에 비용이기도 하다. 즉, shared memory 최적화는 공짜가 아니라 block 협업과 synchronization 비용을 감수하면서 global memory 왕복을 줄이는 전략이다.

실제 성능 향상은 어디서 오는가

tiled matmul의 핵심 성능 이득은 수학이 바뀌어서가 아니다. 데이터 흐름이 바뀌었기 때문이다.

  • global memory load 수 감소
  • shared memory를 통한 재사용 증가
  • coalesced load 설계 가능
  • arithmetic unit가 더 바쁘게 유지됨

이 네 가지가 합쳐져 naive 구현보다 훨씬 높은 throughput을 만든다.

tile 설계에서 자주 보는 trade-off

실제로는 아래 판단이 계속 필요하다.

  • tile 크기를 키워 재사용을 늘릴 것인가
  • occupancy를 남기기 위해 자원 사용을 줄일 것인가
  • thread 하나가 output 원소 하나만 맡을 것인가, 여러 개를 맡길 것인가
  • register blocking까지 함께 도입할 것인가

이 시점부터 matmul 최적화는 거의 하나의 설계 문제처럼 느껴진다.

practical한 시각

실무적으로는 tiled matmul을 볼 때 단순히 "shared memory를 쓴다"보다 아래 흐름을 보는 편이 좋다.

  • 어떤 입력 조각을 tile로 가져오는가
  • 그 조각이 block 안에서 얼마나 재사용되는가
  • synchronization 비용을 감수할 가치가 있는가
  • tile 크기가 occupancy와 register pressure를 어떻게 바꾸는가

이 질문이 있어야 tiled matmul을 템플릿 암기로 보지 않게 된다.

정리

tiled matrix multiply는 GPU 최적화의 대표 예제인 이유가 분명하다.

  • shared memory를 왜 쓰는지 보여준다
  • block 협업이 왜 중요한지 보여준다
  • memory traffic를 줄이는 구조적 방법을 보여준다
  • 자원 사용 trade-off를 동시에 드러낸다

다음 글에서는 shared memory를 쓸 때 자주 마주치는 bank conflict를 따로 떼어 더 자세히 본다.