CUDA Thread Hierarchy
CUDA Thread Hierarchy
GPU에서 thread hierachy가 왜 이리 헷갈리는지 정리하고 넘어가자
Overview
Kernel 실행시, 새로운 Grid가 하나 생성되는 거야.
- Grid: 커널이 실행되는 공간 (kernel 실행시 Gird가 생성)
- Block: 여러개의 워프들의 그룹, 같은 block 내의 thread들은 Shared Memory 같이 쓸수 있다. Block은 쪼개지지 않고 SM 하나에 통째로 배정돼서 실행
- Warp: Thread 32개씩 묶어서 하드웨어적으로 동시실행
- Thread: 개별 연산 수행, 블록 내 shared memory로 소통
Thread
- thread 별로 독립적인 register을 가짐,
threadIdx.x와 같은 변수로 자기 자신의 고유 번호 식별
Warp
- Hardware가 실제로 스레드들을 묶어서 실행하는 단위 (32개)
- 32개의 thread가 동시에, 똑같은 instruction을 실행
Block
- 같은 block에 속한 thread들은 Shared Memory를 공유해
__syncthreads()와 같이 block내 thread들 끼리 동기화 할수 있어- 쪼개지지 않고 하나의 SM에 통째로 배정돼 실행 돼
Grid
- 하나의 kernel을 호출할때 생겅되는 전체 작업 공간
- Block들 끼리는 완전히 독립적이라서 서로 소통이나 동기화 불가
- Block들이 1번 부터 순서대로 실행될지, 무작위로 실행될지는 전적으로 GPU scheduler가 결정
Block이라는 단위가 필요한 이유
- SM과의 1:1 매칭을 위해서
- Block 없이 warp 단위로 matching 시키면, 중앙 스케쥴러, 수많은 warp들을 추적, 관리 해야한는데, 비용이 엄청 클꺼야
- Shared Memory를 쓰기 위해서
- Warp 단위로 하면 shared에 참여하는 범위가 작고, grid 단위는 너무 커서 global memory를 사용해야해
- 확실한 동기화를 위해서
- Grid 전체로 하기에는 deadlock의 위험도 있고 너무 느려
- Transparent Scalability
- Block들이 완전히 독립적이여서, 하드웨어 성능(SM의 갯수)에 맞춰서 유연하게 분배될 수 있는 확장성을 가지게 됨
- Block은 SM의 메모리를 할당 받기 위한 큼직한 덩어리이고, Warp은 실제로 명령어를 싱행하기 위해 하드웨어가 잘게 쪼갠 단위.
뭔가 이상한 conclusion
1
2
3
4
5
6
7
__global__ void addition(int* A, int* B, int* C, int totalElements) {
int workIndex = threadIdx.x + blockDim.x * blockIdx.x;
if(workIndex < totalElements) {
C[workIndex] = A[workIndex] + B[workIndex];
}
}
Grid는 CPU에서 kernel이 실행될때 생기기 때문에, workIndex는 무조건 0부터 시작함이 보장 된다.
This post is licensed under CC BY 4.0 by the author.