[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 관점)

a. 이미지 → 텐서 → .cuda(non_blocking=True)

  • 이때 GPU의 vRAM에 메모리가 할당됨

b. model(batch) 실행

  • 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이 실제 연산 중인 비율 1030% → 병렬 여유 있음 / 80100% → 포화
vRAM 사용량 전체 메모리 중 사용 중인 용량 지속 증가 → 누수 가능성 / 반복적 사용 → 안정
memory_allocated 현재 텐서가 실제 점유 중인 메모리 실질적인 현재 작업 메모리량
memory_reserved PyTorch가 확보한 총 메모리 (캐시 포함) 이 수치가 크면 캐시가 쌓여 있음
latency (1건) 요청 → 응답까지 소요 시간 200~300ms 이상이면 병목 의심
throughput (req/sec) 초당 처리 가능한 요청 수 적절한 batch/stream 조합으로 증가 가능