GPU 시스템 02 - Thread, Warp, Block 실행 모델
GPU의 thread, warp, block, grid가 실제 실행에서 어떤 의미를 가지는지
GPU에서 가장 먼저 헷갈리는 것
CUDA를 처음 볼 때 가장 먼저 마주치는 용어가 thread, warp, block, grid다. 문법 수준에서는 각각을 "실행 단위" 정도로 외우고 넘어갈 수 있다. 하지만 실제 커널 성능을 이해하려면 이 개념들이 단순한 이름표가 아니라 실행 방식 자체를 설명한다는 점을 알아야 한다.
CPU에서는 보통 몇 개의 강한 코어가 순서를 제어하며 계산을 수행한다. 반면 GPU는 아주 많은 thread를 한꺼번에 쏟아내고, 이를 묶어서 실행 효율을 끌어올린다. 이때 가장 중요한 묶음이 warp다.
Thread는 가장 작은 논리 단위다
CUDA 코드에서는 보통 thread 하나가 데이터 원소 하나 또는 작은 작업 하나를 담당한다.
예를 들어 vector add에서는 thread 하나가 다음과 같은 일을 한다.
- 입력 벡터의 한 원소를 읽는다
- 두 값을 더한다
- 결과를 출력 벡터에 쓴다
이 단계에서는 "thread가 많다"는 것만 보이기 쉽다. 하지만 GPU는 thread를 완전히 독립적으로 하나씩 실행하지 않는다.
Warp는 실제 스케줄링의 핵심이다
NVIDIA GPU에서는 보통 32개의 thread가 warp 하나로 묶인다. 실제 하드웨어 스케줄링은 thread 하나보다 warp를 기준으로 보는 편이 훨씬 가깝다.
이 말의 의미는 간단하다. 코드상으로는 32개 thread가 따로 있는 것처럼 보여도, 하드웨어는 이들을 비슷한 흐름으로 밀어붙인다. 그래서 warp 내부에서 분기가 갈리면 성능이 나빠지기 쉽다.
예를 들어 32개 thread 중 절반은 if 경로로 가고 절반은 else 경로로 가면, 하드웨어는 두 경로를 사실상 따로 실행해야 한다. 이것이 warp divergence다.
그래서 GPU 코드를 읽을 때는 항상 "thread 하나가 무슨 일을 하는가"와 함께 "같은 warp의 다른 thread들은 같은 흐름으로 가는가"를 같이 봐야 한다. 이 질문이 없으면 divergence 문제를 너무 늦게 보게 된다.
Block은 협업과 자원 배치의 단위다
block은 여러 warp를 묶는 상위 단위다. block이 중요한 이유는 두 가지다.
첫째, 같은 block 안의 thread들은 shared memory를 함께 사용할 수 있다.
둘째, block은 보통 한 SM 안에 배치되어 동작한다. 즉, block 크기를 어떻게 잡느냐에 따라 shared memory 사용량, register 사용량, occupancy가 함께 영향을 받는다.
예를 들어 block을 너무 크게 잡으면 thread 수는 많아 보이지만, shared memory나 register를 많이 먹어서 동시에 올라갈 수 있는 block 수가 줄어들 수 있다.
이 점 때문에 block은 단순 grouping 단위가 아니라 자원 설계 단위처럼 봐야 한다. block size를 바꾼다는 것은 thread 수를 바꾸는 것인 동시에, shared memory 사용 구조와 occupancy 가능성까지 같이 건드리는 일이다.
Grid는 전체 작업의 범위다
grid는 커널 launch 전체를 나타낸다. 다시 말해 "이번 커널에서 몇 개 block을 던질 것인가"를 결정하는 단위다.
보통은 다음처럼 생각하면 된다.
- thread: 가장 작은 작업 담당자
- warp: 실제 실행에서 중요한 묶음
- block: 협업과 자원 관리 단위
- grid: 전체 문제를 덮는 범위
이 계층을 머릿속에 넣고 커널을 보면 코드가 훨씬 덜 추상적으로 보인다.
왜 이 모델이 성능과 직접 연결될까
이 실행 모델은 단순한 용어 정리가 아니다. 나중에 성능 문제가 생기면 거의 항상 여기로 다시 돌아오게 된다.
예를 들면:
- warp divergence가 심한가?
- block 크기가 너무 커서 occupancy가 떨어지는가?
- block 내부 협업이 shared memory를 제대로 활용하는가?
- grid 크기가 너무 작아서 GPU를 충분히 못 채우는가?
이 질문들은 모두 thread, warp, block 구조를 이해해야 답할 수 있다.
간단한 mental model
실무적으로는 이렇게 기억해도 좋다.
- 코드는 thread 단위로 쓴다
- 성능은 warp 단위에서 무너질 수 있다
- 최적화는 block 단위 자원 제약과 같이 봐야 한다
- 전체 처리량은 grid가 GPU를 얼마나 잘 채우는지와 연결된다
이 감이 생기면 단순한 vector add 커널도 더 이상 단순하지 않게 보인다. 작은 예제 속에도 실제 성능 문제의 씨앗이 다 들어 있기 때문이다.
다음 글에서는 global memory, shared memory, register, bandwidth를 중심으로 GPU 메모리 계층을 본다.