💡 애플리케이션 레벨 GPU 락(직렬화) 하나만 제거하고 vLLM으로 교체해 p95 지연시간 96.2% 단축

RTX 5090 데스크탑 GPU로 실측, 클라우드 비용 없이 증명


🎯 Performance Overview

지표 BEFORE (naive HF) AFTER (vLLM) 개선율
처리 요청 수 (60초) 23건 397건 17배
p50 29,000ms 1,600ms 94.5%↓
p95 50,000ms 1,900ms 96.2%↓
p99 50,000ms 2,200ms 95.6%↓
큐 대기 평균 26,549.3ms 0.0ms 100% 제거

Impact Summary: 코드 변경은 락 제거뿐이었지만, 서빙 엔진 교체만으로 동일 GPU에서 처리량 17배, p95 지연시간 96.2% 개선을 실측으로 증명했다.


🧩 Problem

AI 서비스 성능 최적화(GPU 추론 최적화, 파인튜닝 모델 서빙) 프리랜서 공고에 지원하려 했으나, 관련 경험을 증명할 포트폴리오가 없었다. 클라우드 GPU 대여 비용 없이, 보유 중인 데스크탑 GPU(RTX 5090, 32GB VRAM)로 직접 계측·증명하기로 했다.


🔧 Approach & Solution

왜 이렇게 설계했는가: 최적화 효과를 명확히 증명하려면 "무엇을 바꿔서 얼마나 좋아졌는지"가 분리되어야 한다. 그래서 실제 병목을 찾아 최적화하는 대신, 배칭 없는 순차 서빙을 의도적으로 BEFORE로 재현하고 서빙 엔진만 vLLM으로 교체하는 최소 변경 실험으로 설계했다.

# BEFORE: GPU를 직렬화하는 락
_gpu_lock = asyncio.Lock()

async with _gpu_lock:
    answer, generation_time = await loop.run_in_executor(
        None, _generate_sync, prompt
    )

# AFTER: 락 제거, vLLM이 자체적으로 continuous batching 처리
answer, generation_time = await _generate_via_vllm(prompt)

구현 단계

  1. 검색(mock) → 리랭킹(BAAI/bge-reranker-base, 실제 GPU 연산) → 생성(LLM) 파이프라인 구현
  2. BEFORE: HuggingFace Transformers generate()를 asyncio.Lock으로 직렬화
  3. AFTER: 생성 단계만 vLLM으로 교체
  4. Locust 부하테스트 + pynvml GPU 모니터링을 별도 프로세스로 분리해 측정 오염 방지
  5. 요청 단위 stage별(검색/리랭킹/큐대기/생성) 타이밍을 API 응답에 포함