[GPU 서버 이전] GPU‐Cuda의 이해 - 100-hours-a-week/5-yeosa-wiki GitHub Wiki
1. GPU의 구조
a. SM (Streaming Multiprocessor)
- GPU의 핵심 연산 유닛. T4 기준으로 40개 존재
- 각 SM은 여러 개의 CUDA Core, warp scheduler, shared memory, register를 가짐
- 동시에 여러 thread block을 처리할 수 있음
b. CUDA Core
- SM 내부에서 워프 단위 명령어를 처리하는 연산 유닛 (T4 기준 SM당 64개)
c. vRAM (GPU Memory)
- CUDA 텐서, 모델 가중치, 연산 중간결과가 저장됨
- CPU 메모리와는 별도, PCIe로 연결됨
- 텐서를
.cuda()
하면 이 메모리에 로드됨
2. GPU 용어 정리
용어 | 설명 |
---|---|
스트림(Stream) | GPU 연산을 순서대로 실행하기 위한 작업 큐. 서로 다른 스트림은 병렬 실행 가능 |
커널(Kernel) | GPU에서 실행될 함수. 수천 개 스레드가 병렬로 실행함 |
스레드 블록 | 커널이 분할되어 실행되는 단위. 하나의 SM에 배정됨 |
스레드(Thread) | block 내부의 가장 작은 실행 단위. 보통 1 block = 수백 개 thread |
워프(Warp) | 32개의 thread로 구성. 실제 실행은 warp 단위로 진행됨 |
3. PyTorch 추론 실행 흐름 (GPU 관점)
.cuda(non_blocking=True)
a. 이미지 → 텐서 → - 이때 GPU의 vRAM에 메모리가 할당됨
model(batch)
실행
b. - PyTorch가 내부적으로 여러 CUDA 커널을 launch
- 커널은 **디폴트 스트림(또는 지정 스트림)**에 들어감
c. 커널 실행 과정
- block 단위로 쪼개져 SM에 분산 배치
- 각 block은 warp로 쪼개져 CUDA core에서 병렬 실행됨
d. CUDA caching allocator
- 사용된 GPU memory는 즉시 해제되지 않고 캐시에 저장
- 재사용 가능하지만,
memory_allocated()
는 줄고memory_reserved()
는 유지됨
4. 단일 모델 인스턴스의 한계
- PyTorch는 기본적으로 디폴트 스트림을 사용
- 모델 인스턴스가 여러 개여도 같은 stream 사용 시 직렬 처리
- → 여러 요청이 들어와도 GPU 커널은 순차 실행
5. Semaphore가 유효한가
- 추론은 직렬이지만, 요청마다
.cuda()
등으로 vRAM 사용 - 여러 요청이 동시에 들어오면 VRAM 오버플로우, allocator 락 경합 발생 가능
- 세마포어로 동시에 실행되는 요청 수 제한 → 안정성 보장, OOM 방지
6. 병렬화 방법
a. 여러 스트림 사용 (추천)
torch.cuda.Stream()
으로 다중 스트림 생성with torch.cuda.stream(...)
으로 분리 실행 → 병렬 커널 제출 가능
b. 모델 멀티 인스턴스 ?
- 같은 디바이스에서 실행되면 stream이 같아서 효과 없음
- 단, stream을 분리해 지정한다면 의미 있음
- 가중치가 중복으로 올라가므로 비효율적
7. 추론 속도 향상 전략
방법 | 설명 | 장점 | 단점 |
---|---|---|---|
CUDA Graph | 반복되는 동일 batch 연산을 그래프에 캡처 후 재사용 | launch overhead 제거 | 입력 shape이 고정되어야 함 |
Dynamic Batching | 여러 요청을 일정 시간 모아 batch 처리 | throughput 극대화 | latency 증가 가능 |
non_blocking=True |
비동기 복사 (pinned memory 필요) | 데이터 전송 겹침 가능 | pinned memory 필요 |
TensorRT | ONNX 변환 후 최적화 엔진으로 추론 | 2~10배 빠름 | 변환 및 유지가 번거로움 |
8. 도입 고려 전략
전략 | 상세 설명 | 적용 이유 |
---|---|---|
Pinned Memory + non_blocking | CPU 메모리를 page-locked로 만들면 GPU가 직접 접근 가능 (DMA) → .cuda(non_blocking=True) 활성화됨 |
이미지 수십 장을 빠르게 전송 가능 |
Batch 처리 고정 | 32장씩 고정 배치 → CUDA Graph 활용 가능 | 추론 속도 향상, 메모리 안정성 |
Stream Pool | 2~4개의 stream을 미리 생성해 요청 분산 처리 | 디폴트 스트림 직렬 처리 한계 해소 |
memory_allocator 조절 | 메모리 재사용 고려. 필요 시 torch.cuda.empty_cache() |
긴 시간 서버 운영 시 메모리 누수 방지 |
9. 도입 시 모니터링할 지표들
지표 | 설명 | 해석 기준 |
---|---|---|
GPU Utilization | SM이 실제 연산 중인 비율 | 10 |
vRAM 사용량 | 전체 메모리 중 사용 중인 용량 | 지속 증가 → 누수 가능성 / 반복적 사용 → 안정 |
memory_allocated | 현재 텐서가 실제 점유 중인 메모리 | 실질적인 현재 작업 메모리량 |
memory_reserved | PyTorch가 확보한 총 메모리 (캐시 포함) | 이 수치가 크면 캐시가 쌓여 있음 |
latency (1건) | 요청 → 응답까지 소요 시간 | 200~300ms 이상이면 병목 의심 |
throughput (req/sec) | 초당 처리 가능한 요청 수 | 적절한 batch/stream 조합으로 증가 가능 |