
강의 영상 보기 (새 탭에서 재생)YouTube
📓Google Colab에서 실습하기
이 레슨은 PyTorch/GPU가 필요합니다. 노트북을 다운로드 후 Google Colab에서 열어주세요.
학습 내용
CUDA 개요
학습 목표
- •CUDA 플랫폼의 개념을 이해한다
- •Host와 Device의 관계를 이해한다
- •커널 함수의 개념을 배운다
- •스레드 계층 구조(Thread, Block, Grid)를 이해한다
- •threadIdx, blockIdx, blockDim의 활용법을 익힌다
CUDA란?
**CUDA(Compute Unified Device Architecture)**는 NVIDIA가 개발한 병렬 컴퓨팅 플랫폼입니다.
CUDA의 특징
| 특징 | 설명 |
|---|---|
| 개발사 | NVIDIA |
| 목적 | GPU를 범용 연산에 활용 |
| 언어 | C/C++ 확장 문법 |
| 등장 | 2007년 |
CUDA가 아니었다면?
과거: GPU는 그래픽 처리 전용
→ 범용 연산 불가능
CUDA 등장 후: GPU를 일반 연산에 활용
→ 딥러닝 혁명의 기반
→ 과학 시뮬레이션 가속
→ 영상 처리 실시간화
Host와 Device
CUDA 프로그래밍에서는 두 가지 처리 장치를 사용합니다.
용어 정의
| 용어 | 의미 | 역할 |
|---|---|---|
| Host | CPU + 시스템 메모리 | 프로그램 제어, 데이터 준비 |
| Device | GPU + GPU 메모리 | 병렬 연산 수행 |
실행 흐름
┌─────────── Host (CPU) ───────────┐
│ 1. 데이터 준비 │
│ 2. GPU 메모리 할당 │
│ 3. Host → Device 데이터 복사 │
│ 4. 커널 실행 명령 │
│ 5. Device → Host 결과 복사 │
│ 6. 결과 후처리 │
└──────────────────────────────────┘
↕ PCIe / NVLink
┌─────────── Device (GPU) ─────────┐
│ 병렬 연산 수행 │
│ (수천 개 스레드 동시 실행) │
└──────────────────────────────────┘
메모리 분리
Host Memory (RAM) Device Memory (VRAM)
┌─────────────┐ ┌─────────────┐
│ data[] │ ────→ │ d_data[] │
│ result[] │ ←──── │ d_result[] │
└─────────────┘ └─────────────┘
CPU용 GPU용
커널 함수
**커널(Kernel)**은 GPU에서 실행되는 함수입니다.
커널의 특징
- •
__global__키워드로 선언 - •Host에서 호출하고 Device에서 실행
- •여러 스레드가 동시에 같은 코드를 실행
커널 선언 예시
cuda// __global__ 키워드로 커널 함수 선언 __global__ void vectorAdd(float* a, float* b, float* c, int n) { int idx = threadIdx.x + blockIdx.x * blockDim.x; if (idx < n) { c[idx] = a[idx] + b[idx]; // 각 스레드가 하나의 원소 처리 } }
함수 실행 위치 지정자
| 지정자 | 호출 위치 | 실행 위치 | 용도 |
|---|---|---|---|
__global__ | Host | Device | 커널 함수 |
__device__ | Device | Device | GPU 내부 헬퍼 함수 |
__host__ | Host | Host | 일반 CPU 함수 (기본값) |
스레드 계층 구조
CUDA는 3단계 계층 구조로 스레드를 조직합니다.
Thread → Block → Grid
┌─────────────────────────────────────────────────────┐
│ Grid (그리드) │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Block 0 │ │ Block 1 │ │ Block 2 │ ... │
│ │ ┌─┬─┬─┬─┐ │ │ ┌─┬─┬─┬─┐ │ │ ┌─┬─┬─┬─┐ │ │
│ │ │T│T│T│T│ │ │ │T│T│T│T│ │ │ │T│T│T│T│ │ │
│ │ ├─┼─┼─┼─┤ │ │ ├─┼─┼─┼─┤ │ │ ├─┼─┼─┼─┤ │ │
│ │ │T│T│T│T│ │ │ │T│T│T│T│ │ │ │T│T│T│T│ │ │
│ │ └─┴─┴─┴─┘ │ │ └─┴─┴─┴─┘ │ │ └─┴─┴─┴─┘ │ │
│ └───────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────┘
T = Thread (스레드)
계층별 특징
| 계층 | 설명 | 공유 메모리 |
|---|---|---|
| Thread | 최소 실행 단위 | 레지스터, 로컬 메모리 |
| Block | 스레드의 그룹 (최대 1024개) | Shared Memory 공유 |
| Grid | 블록의 그룹 | Global Memory 공유 |
스레드 인덱스 변수
각 스레드는 자신의 위치를 알 수 있는 변수를 가집니다.
핵심 내장 변수
| 변수 | 의미 | 타입 |
|---|---|---|
threadIdx | 블록 내 스레드 인덱스 | dim3 (x, y, z) |
blockIdx | 그리드 내 블록 인덱스 | dim3 (x, y, z) |
blockDim | 블록의 크기 | dim3 (x, y, z) |
gridDim | 그리드의 크기 | dim3 (x, y, z) |
전역 인덱스 계산
cuda// 1D 배열에서 전역 인덱스 계산 int globalIdx = threadIdx.x + blockIdx.x * blockDim.x; // 예시: blockDim.x = 256, blockIdx.x = 3, threadIdx.x = 100 // globalIdx = 100 + 3 * 256 = 868
시각적 이해
Block 0 Block 1 Block 2
[T0,T1,T2,T3] [T0,T1,T2,T3] [T0,T1,T2,T3] (threadIdx.x)
↓ ↓ ↓
[0, 1, 2, 3] [4, 5, 6, 7] [8, 9,10,11] (globalIdx)
커널 실행 구문
커널은 **실행 구성(Execution Configuration)**과 함께 호출됩니다.
실행 구문 형식
cudakernelFunction<<<gridSize, blockSize>>>(arguments); // └──────┬──────┘ // 그리드 크기, 블록 크기
예시: 10,000개 원소 처리
cudaint n = 10000; int blockSize = 256; // 블록당 256 스레드 int gridSize = (n + blockSize - 1) / blockSize; // 올림 나눗셈 // gridSize = (10000 + 255) / 256 = 40 vectorAdd<<<gridSize, blockSize>>>(d_a, d_b, d_c, n); // 40개 블록 × 256 스레드 = 10,240 스레드 실행 // (240개는 경계 검사로 무시됨)
2D/3D 실행 구성
cuda// 이미지 처리: 1920×1080 픽셀 dim3 blockSize(16, 16); // 16×16 = 256 스레드/블록 dim3 gridSize( (1920 + 15) / 16, // 120 블록 (1080 + 15) / 16 // 68 블록 ); imageProcess<<<gridSize, blockSize>>>(image, width, height); // 커널 내부 __global__ void imageProcess(...) { int x = threadIdx.x + blockIdx.x * blockDim.x; int y = threadIdx.y + blockIdx.y * blockDim.y; // 각 스레드가 하나의 픽셀 처리 }
완전한 CUDA 프로그램 구조
cuda#include <cuda_runtime.h> #include <stdio.h> // 1. 커널 함수 정의 __global__ void vectorAdd(float* a, float* b, float* c, int n) { int idx = threadIdx.x + blockIdx.x * blockDim.x; if (idx < n) { c[idx] = a[idx] + b[idx]; } } int main() { int n = 10000; size_t size = n * sizeof(float); // 2. Host 메모리 할당 및 초기화 float *h_a = (float*)malloc(size); float *h_b = (float*)malloc(size); float *h_c = (float*)malloc(size); // 3. Device 메모리 할당 float *d_a, *d_b, *d_c; cudaMalloc(&d_a, size); cudaMalloc(&d_b, size); cudaMalloc(&d_c, size); // 4. Host → Device 복사 cudaMemcpy(d_a, h_a, size, cudaMemcpyHostToDevice); cudaMemcpy(d_b, h_b, size, cudaMemcpyHostToDevice); // 5. 커널 실행 int blockSize = 256; int gridSize = (n + blockSize - 1) / blockSize; vectorAdd<<<gridSize, blockSize>>>(d_a, d_b, d_c, n); // 6. Device → Host 복사 cudaMemcpy(h_c, d_c, size, cudaMemcpyDeviceToHost); // 7. 메모리 해제 cudaFree(d_a); cudaFree(d_b); cudaFree(d_c); free(h_a); free(h_b); free(h_c); return 0; }
정리
| 개념 | 설명 |
|---|---|
| CUDA | NVIDIA의 GPU 병렬 컴퓨팅 플랫폼 |
| Host/Device | CPU/GPU와 각각의 메모리 |
| 커널 | GPU에서 실행되는 __global__ 함수 |
| Thread | 최소 실행 단위 |
| Block | 스레드 그룹 (최대 1024개) |
| Grid | 블록 그룹 |
| threadIdx | 블록 내 스레드 위치 |
| blockIdx | 그리드 내 블록 위치 |
| blockDim | 블록 크기 |
핵심 포인트: CUDA는 계층적 스레드 구조를 통해 수천 개의 스레드가 동시에 작업을 분담합니다.
레슨 정보
- 레벨
- Level 8: GPU 프로그래밍 (CUDA 기초)
- 예상 소요 시간
- 50분
- 참고 영상
- YouTube 링크
💡실습 환경 안내
이 레벨은 PyTorch/GPU가 필요하여 Google Colab 사용을 권장합니다.
Colab은 무료 GPU를 제공하여 PyTorch, CNN, Transformer 등을 실행할 수 있습니다.