[llm-compressor] vLLM LLM-Compressor: CUDA OOM 문제 해결을 위한 메모리 핀닝 최적화
PR 링크: vllm-project/llm-compressor#2813 상태: Merged | 변경: +65 / -19
들어가며
최근 llm-compressor 라이브러리를 사용하여 대규모 모델에 AWQ(Activation-aware Weight Quantization)를 적용하는 과정에서, 특히 DDP(Distributed Data Parallel) 환경에서 CUDA OOM(Out of Memory) 문제가 빈번하게 발생하는 이슈가 보고되었습니다. 분석 결과, 기존 코드에서 모든 중간 활성화 값(Intermediate Activations)을 미리 pin_memory()로 고정(pinning)하던 과정이 시스템 메모리 부족을 유발하고 있었습니다. 본 PR은 이 메모리 핀닝 시점을 최적화하여, 필요한 시점에만 메모리를 핀닝함으로써 OOM 문제를 해결하고 효율적인 H2D(Host-to-Device) 전송을 가능하게 했습니다.
코드 분석
1. src/llmcompressor/pipelines/cache.py: 지연 핀닝 구현
기존에는 _offload_value 내부에서 무조건적으로 pin_memory()를 호출했습니다. 이를 제거하고, 대신 IntermediatesCache 클래스에 명시적인 pin_memory() 메서드를 추가하여 필요한 시점에만 호출하도록 변경했습니다.
Before:
# 기존: offload 시점에 무조건 핀닝
if torch.device(offload_device).type == "cpu" and not offloaded.is_pinned():
offloaded = offloaded.pin_memory()
After:
# 변경: 명시적 호출을 통한 지연 핀닝
def pin_memory(self, batch_index: int, input_names: list[str] | None = None) -> None:
intermediates = self.batch_intermediates[batch_index]
for key, intermediate in intermediates.items():
if input_names is None or key in input_names:
self._pin_intermediate(intermediate)
2. src/llmcompressor/modifiers/transform/awq/base.py: 최적화된 호출 시점
AWQ의 그리드 서치(Grid Search) 과정에서 반복적으로 활성화 값을 사용하므로, 서치 직전에 해당 배치를 핀닝하도록 수정했습니다.
After:
# AWQ 그리드 서치 직전에 핀닝 수행
cache = self._parent_args_cache[parent_module]
for batch_index in range(len(cache)):
cache.pin_memory(batch_index)
왜 이게 좋은가
1. CUDA OOM 해결 및 안정성 확보
기존 방식은 모든 데이터를 미리 핀닝하여 시스템 RAM을 과도하게 점유했습니다. 특히 대규모 모델(예: Qwen-30B)에서는 이로 인해 프로세스가 강제 종료되는 문제가 있었습니다. 이번 변경을 통해 메모리 점유율을 최적화하여 대규모 모델에서도 안정적인 학습이 가능해졌습니다.
2. 비동기 데이터 전송(Non-blocking H2D) 활용
pin_memory()를 적절히 사용함으로써, non_blocking=True 옵션을 통한 DMA(Direct Memory Access) 전송이 가능해졌습니다. 이는 GPU 연산과 데이터 전송을 오버랩(Overlap)시켜 전체적인 파이프라인 성능을 개선합니다.
3. 성능 지표
실제 벤치마크 결과, non_blocking=False를 사용했을 때 약 4시간이 소요되던 작업이, 이번 최적화를 통해 37분 수준으로 대폭 단축되었습니다. 비록 단일 GPU 환경에서는 메인 브랜치 대비 미세한 성능 차이가 있을 수 있으나, 대규모 모델에서의 안정성 확보라는 측면에서 매우 가치 있는 트레이드오프입니다.
교훈
pin_memory()는 CPU-GPU 간 데이터 전송 속도를 높이는 강력한 도구이지만, 시스템 RAM을 고정시키므로 무분별하게 사용하면 OOM을 유발합니다. "필요한 데이터만, 필요한 시점에" 핀닝하는 지연 전략이 대규모 모델 최적화의 핵심임을 다시 한번 확인했습니다.
참고 자료
⚠️ 알림: 이 분석은 AI가 실제 코드 diff를 기반으로 작성했습니다.
관련 포스트
- [vllm] vLLM DeepSeek-V4 성능 최적화: 불필요한 torch.full 커널 제거로 1.88배 속도 향상
- [vllm] vLLM 성능 최적화: token_to_req_indices 캐싱을 통한 6배 성능 향상
- [vllm] vLLM의 GDN 어텐션 최적화: Prefill과 Decode 배치 분리를 통한 2배 성능 향상
- [vllm] vLLM의 FP8 Scaled MM 최적화: Padding 제거를 통한 20% 성능 향상
- [vllm] [vLLM] MiniMax-M2 MoE Gate 최적화: Fused FP32 Kernel로 서빙 성능 32% 향상시키기
PR Analysis 의 다른글
- 이전글 [ray] Ray Serve LLM 인그레스 최적화: 로컬 라우팅을 통한 병목 현상 제거
- 현재글 : [llm-compressor] vLLM LLM-Compressor: CUDA OOM 문제 해결을 위한 메모리 핀닝 최적화
- 다음글 [vllm] vLLM의 KV 캐시 로드 경로 최적화: NumPy를 활용한 Vectorization
댓글