CUDA
contents
CUDA (Compute Unified Device Architecture) 는 NVIDIA가 만든 병렬 컴퓨팅 플랫폼이자 애플리케이션 프로그래밍 인터페이스(API) 모델입니다. 이를 통해 소프트웨어 개발자는 CUDA가 지원되는 그래픽 처리 장치(GPU)를 범용 처리(GPGPU, General-Purpose computing on Graphics Processing Units)에 사용할 수 있습니다.
간단히 말해, CUDA는 그래픽 카드의 엄청난 병렬 처리 능력을 잠금 해제하여 표준 CPU보다 훨씬 빠르게 복잡한 수학 문제를 해결할 수 있게 해줍니다.
1. 핵심 철학: CPU vs. GPU
CUDA를 이해하려면 하드웨어의 차이점을 이해해야 합니다.
- CPU (중앙 처리 장치): 지연 시간(Latency) 을 줄이는 데 설계되었습니다. 순차적 처리, 분기 로직(if/else), 운영체제(OS) 처리에 뛰어난 소수의 강력한 코어(예: 8~16 코어)를 가지고 있습니다. 소수의 사람들을 목적지로 빠르게 이동시키는 페라리와 같습니다.
- GPU (그래픽 처리 장치): 처리량(Throughput) 을 높이는 데 설계되었습니다. 수천 개의 더 작고 단순한 코어(예: 10,000개 이상의 코어)를 가지고 있습니다. 복잡한 로직에는 서툴지만, 수백만 개의 데이터 포인트에 대해 동일한 단순 수학 연산을 동시에 수행하는 데는 놀라울 정도로 뛰어납니다. 한 사람당 속도는 느리지만 수천 명을 한 번에 이동시키는 버스 시스템과 같습니다.
CUDA는 다리(Bridge)입니다. C, C++, Python, Fortran 코드가 이 수천 개의 GPU 코어에 명령을 내릴 수 있게 해줍니다.
2. CUDA 아키텍처: 하드웨어 및 소프트웨어 계층
A. 하드웨어 관점 (GPU)
CUDA 관점에서 GPU는 계층적으로 구성됩니다.
- SM (스트리밍 멀티프로세서): NVIDIA GPU의 기본 구성 블록입니다. GPU는 많은 SM을 포함합니다. 각 SM은 많은 CUDA 코어를 포함합니다.
- CUDA 코어: 정수 및 부동 소수점 연산을 실행하는 작은 유닛입니다.
- 텐서 코어 (Tensor Cores): 최신 GPU(Volta, Ampere, Hopper 등)에 있는 특수 코어로, 행렬 곱셈(AI/딥러닝)을 위해 특별히 설계되었습니다.
- RT 코어: 레이 트레이싱(Ray Tracing)을 위한 특수 코어입니다.
- 메모리:
- 글로벌 메모리 (VRAM): 크지만(예: 24GB), 모든 코어가 접근 가능하며 느립니다(높은 지연 시간).
- 공유 메모리 (L1 캐시): 각 SM _내부_에 위치한 작고 매우 빠른 메모리입니다. 블록 내의 스레드들이 서로 통신하는 데 사용할 수 있습니다.
- 레지스터: 스레드별로 비공개인 가장 빠른 메모리입니다.
B. 소프트웨어 관점 (그리드)
CUDA 프로그램(커널)을 실행할 때, 작업은 논리적으로 나뉩니다.
- 스레드 (Thread): 실행의 가장 작은 단위입니다. 각 스레드는 동일한 코드를 다른 데이터에 대해 실행합니다.
- 블록 (Block): 스레드들의 그룹입니다(예: 128개 또는 256개 스레드). 블록 내의 스레드들은 동일한 SM에서 실행되며 메모리를 공유할 수 있습니다.
- 그리드 (Grid): 블록들의 모음입니다. 이것은 해결하려는 전체 문제를 나타냅니다.
3. CUDA 프로그램의 작동 방식 (워크플로우)
일반적인 CUDA 프로그램은 호스트-디바이스 모델을 따릅니다.
- 호스트 (Host): CPU와 시스템 RAM.
- 디바이스 (Device): GPU와 그 VRAM.
CUDA 프로그램의 4단계:
- 메모리 할당: CPU가
cudaMalloc을 사용하여 GPU(VRAM)에 메모리를 할당합니다. - 데이터 복사: CPU가 시스템 RAM의 입력 데이터를 GPU VRAM으로 복사합니다(
cudaMemcpy사용). - 커널 실행: CPU가 GPU에게 수천 개의 스레드에 걸쳐 함수(커널)를 실행하라고 지시합니다. GPU가 숫자를 처리하는 동안 CPU는 다른 작업을 계속할 수 있습니다.
- 복사 및 회수: GPU 작업이 완료되면, CPU는 결과를 VRAM에서 시스템 RAM으로 다시 복사해 옵니다.
4. 코딩 예제 (CUDA C++)
두 개의 거대한 배열을 더하는(벡터 덧셈) 간단한 예제입니다.
표준 C++ (CPU):
void add(int n, float *x, float *y) {
for (int i = 0; i < n; i++)
y[i] = x[i] + y[i];
}
CUDA C++ (GPU):
// 커널 (GPU에서 실행됨)
// __global__은 이 함수가 디바이스에서 실행되고 호스트에서 호출됨을 컴파일러에 알립니다.
__global__ void add(int n, float *x, float *y) {
// 이 스레드에 대한 전역 인덱스 계산
int index = blockIdx.x * blockDim.x + threadIdx.x;
int stride = blockDim.x * gridDim.x;
// 각 스레드가 특정 요소를 처리
for (int i = index; i < n; i += stride)
y[i] = x[i] + y[i];
}
// 메인 함수 (호스트)
add<<>>(N, x, y); // 커널 실행 (Launch)
cudaDeviceSynchronize(); // GPU가 끝날 때까지 대기
threadIdx.x: 블록 내 스레드의 ID.blockIdx.x: 그리드 내 블록의 ID.blockDim.x: 블록에 얼마나 많은 스레드가 있는지.
5. CUDA가 승리한 이유 (생태계)
CUDA(2007년) 이전에는 GPU를 수학 연산에 사용하려면 그래픽 API(OpenGL/DirectX)를 해킹하여 GPU가 데이터 포인트를 "픽셀"인 것처럼 착각하게 만들어야 했습니다. CUDA는 이를 C 코드처럼 보이게 만들었습니다.
오늘날 CUDA가 지배적인 이유는 생태계 때문입니다.
- 라이브러리: NVIDIA는 커널을 처음부터 작성하지 않아도 되도록 고도로 최적화된 라이브러리를 제공합니다.
- cuBLAS: 기본 선형 대수 서브프로그램 (행렬 연산).
- cuDNN: 심층 신경망 라이브러리 (PyTorch, TensorFlow의 기반).
- cuFFT: 고속 푸리에 변환.
- 도구: Nsight Systems와 Nsight Compute를 통해 개발자는 GPU 코드를 나노초 단위까지 프로파일링하고 디버깅할 수 있습니다.
6. 단점과 과제
- 벤더 종속 (Vendor Lock-in): CUDA는 오직 NVIDIA GPU에서만 작동합니다. AMD나 Intel GPU에서 실행하려면 OpenCL이나 HIP (ROCm)을 사용해야 하지만, CUDA가 훨씬 더 성숙하고 성능이 좋습니다.
- 메모리 병목: GPU 컴퓨팅의 가장 큰 제약은 보통 계산 속도가 아니라, CPU와 GPU 사이(PCIe 버스) 또는 VRAM과 코어 사이(메모리 대역폭)에서 데이터를 이동하는 속도입니다.
- 복잡성: 고도로 최적화된 커널을 작성하려면 하드웨어에 대한 깊은 지식(메모리 병합, 뱅크 충돌, 워프 다이버전스 등)이 필요합니다.
요약
CUDA는 현대 AI 폭발의 연료가 된 "비법 소스"입니다. GPU를 게이머를 위한 장난감에서 과학자를 위한 슈퍼컴퓨터로 변화시킴으로써, 이전에는 계산이 불가능했던 거대 신경망(ChatGPT 같은)의 학습을 가능하게 했습니다.
references