CPU loop에서 GPU kernel로
CUDA를 처음 배울 때 가장 먼저 잡아야 하는 차이는 이것이다.
CPU:
한 실행 흐름이 for-loop로 원소를 하나씩 처리한다.
GPU:
많은 CUDA thread가 같은 kernel 코드를 각자 다른 index로 실행한다.
CPU 방식
배열 두 개를 더하는 CPU 코드는 보통 이렇게 생겼다.
for (int i = 0; i < n; i++) {
c[i] = a[i] + b[i];
}
여기서는 i가 0, 1, 2, ... 순서로 바뀐다. 하나의 loop가 전체 배열을 지나간다.
GPU 방식
GPU에서는 loop의 각 반복을 여러 thread에게 나누어 맡긴다.
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
c[i] = a[i] + b[i];
}
각 thread는 같은 코드를 실행하지만, blockIdx.x와 threadIdx.x가 다르기 때문에 서로 다른 i를 만든다.
thread 0 -> c[0] = a[0] + b[0]
thread 1 -> c[1] = a[1] + b[1]
thread 2 -> c[2] = a[2] + b[2]
...
왜 kernel이 필요한가
CPU 함수는 CPU에서 실행된다. GPU에서 실행할 함수는 CUDA에서 kernel이라고 부른다.
__global__ void vectorAdd(...) {
...
}
__global__은 이 함수가 CPU에서 호출되지만 GPU에서 실행된다는 표시다.
인터랙티브 설계
- 왼쪽에는 CPU loop가
i=0부터 한 칸씩 이동하는 모습을 보여준다. - 오른쪽에는 GPU thread 여러 개가 배열의 여러 칸을 동시에 켜는 모습을 보여준다.
- 같은
c[i] = a[i] + b[i]가 CPU에서는 loop body이고, GPU에서는 각 thread의 작업이라는 점을 강조한다.
확인
- CPU loop에서
i는 누가 바꾸는가? - GPU kernel에서 각 thread의
i는 어떻게 달라지는가? - GPU thread들이 같은 코드를 실행해도 서로 다른 원소를 처리할 수 있는 이유는 무엇인가?