개요
CUDA는 엔비디아가 자체 GPU에서의 일반 컴퓨팅을 위해 개발한 병렬 컴퓨팅 플랫폼이자 프로그래밍 모델입니다. CUDA를 통해 개발자는 연산의 병렬화가 가능한 부분에 GPU의 성능을 활용하여 컴퓨팅 집약적인 애플리케이션의 속도를 높일 수 있습니다.
CUDA 프로그래밍의 핵심 개념
CUDA 프로그래밍을 이해하기 위해서는 몇 가지 핵심 개념을 파악해야 합니다:
1. 커널 (Kernels)
- 커널은 GPU (장치)에서 실행되는 함수입니다.
- 여러 스레드에서 병렬적으로 실행되도록 설계되었습니다.
2. 스레드 계층 구조 (Thread Hierarchy)
- CUDA에서 스레드가 어떻게 구성되는지 나타냅니다.
- 스레드는 블록으로 그룹화되고, 블록은 그리드로 그룹화됩니다.
3. 메모리 계층 구조 (Memory Hierarchy)
- CUDA는 글로벌 메모리, 공유 메모리, 상수 메모리를 포함한 복잡한 메모리 계층 구조를 가지고 있습니다.
- 각 메모리 유형은 액세스 방법에 영향을 미치는 다른 속성을 가지고 있습니다.
4. 이기종 프로그래밍 (Heterogeneous Programming)
- CUDA를 통해 CPU와 GPU 모두에서 실행되는 프로그램을 작성할 수 있습니다.
- 이는 계산 집약적인 부분과 메모리 집약적인 부분으로 작업을 분할할 수 있는 작업에 유용합니다.
5. 비동기 SIMT 프로그래밍 모델 (Asynchronous SIMT Programming Model)
- 단일 명령, 다중 스레드 (SIMT) 프로그래밍 모델은 CUDA의 기본 개념입니다.
- 즉, 단일 명령이 여러 스레드에 의해 동시에 실행될 수 있습니다.
- 비동기 프로그래밍을 통해 커널 실행을 호스트와 장치 간 데이터 전송과 같은 다른 작업과 중첩할 수 있습니다.
6. 계산 능력 (Compute Capability)
- GPU의 계산 능력은 세대와 기능을 나타냅니다.
- CUDA 프로그램을 작성할 때 GPU의 계산 능력을 고려하는 것이 중요합니다. 이는 일부 기능이 이전 GPU에서는 지원되지 않을 수 있기 때문입니다.
7. 성능 최적화 전략 (Performance Optimization Strategies)
- CUDA 프로그램의 성능을 최적화하는 방법에는 여러 가지가 있습니다.
- 여기에는 활용도 극대화, 메모리 처리량 극대화, 명령 처리량 극대화 등이 포함됩니다.
CUDA 프로그래밍의 간단한 예시: 행렬 곱셈
CUDA 프로그래밍의 핵심 개념을 이해하기 위해 간단한 예시를 살펴보겠습니다. 이 예시에서는 두 개의 행렬을 곱하는 작업을 GPU에서 수행합니다.
1. 코드 구조
C++
#include <iostream>
__global__ void matrixMul(float *A, float *B, float *C, int N) {
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
if (row < N && col < N) {
float sum = 0.0f;
for (int k = 0; k < N; k++) {
sum += A[row * N + k] * B[k * N + col];
}
C[row * N + col] = sum;
}
}
int main() {
int N = 1024; // 행렬의 크기
// 행렬 A와 B를 CPU에서 생성 및 초기화
float *A, *B, *C;
A = (float *)malloc(N * N * sizeof(float));
B = (float *)malloc(N * N * sizeof(float));
C = (float *)malloc(N * N * sizeof(float));
// 행렬 A와 B를 GPU 메모리로 전송
float *dA, *dB, *dC;
cudaMalloc((void **)&dA, N * N * sizeof(float));
cudaMalloc((void **)&dB, N * N * sizeof(float));
cudaMalloc((void **)&dC, N * N * sizeof(float));
cudaMemcpy(dA, A, N * N * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(dB, B, N * N * sizeof(float), cudaMemcpyHostToDevice);
// 커널 실행 (GPU에서 행렬 곱셈 수행)
matrixMul<<<(N / blockDim.x, N / blockDim.y), blockDim>>>(dA, dB, dC, N);
// 결과를 GPU 메모리에서 CPU로 전송
cudaMemcpy(C, dC, N * N * sizeof(float), cudaMemcpyDeviceToHost);
// 결과 행렬 C 출력
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
std::cout << C[i * N + j] << " ";
}
std::cout << std::endl;
}
// GPU 메모리 해제
free(A);
free(B);
free(C);
cudaFree(dA);
cudaFree(dB);
cudaFree(dC);
return 0;
}
2. 코드 설명
2.1. __global__
키워드
__global__
키워드는 해당 함수가 GPU에서 실행되는 커널임을 나타냅니다.
2.2. matrixMul
커널 함수
matrixMul
함수는 두 개의 입력 행렬 (A와 B)와 출력 행렬 (C)을 받습니다.blockIdx
와threadIdx
는 각 블록과 스레드의 인덱스를 제공합니다.blockDim
은 각 블록의 크기를 나타냅니다.- 코드는 각 스레드가 행렬의 한 요소를 계산하도록 구성됩니다.
2.3. main
함수
main
함수는 CPU에서 행렬을 생성하고 초기화합니다.- GPU 메모리에 공간을 할당하고 행렬을 GPU 메모리로 전송합니다.
matrixMul
커널을 실행하여 행렬 곱셈을 수행합니다.- 결과를 GPU 메모리에서 CPU로 전송하고 출력합니다.
- 마지막으로 GPU 메모리를 해제합니다.
추가 정보
위의 예시는 CUDA 프로그래밍의 기본적인 개념을 보여주는 간단한 예시입니다. 실제 CUDA 프로그래밍은 더 복잡할 수 있으며, 더 많은 기능과 최적화 기술을 포함할 수 있습니다.