[vllm] vLLM 성능 최적화: Thread Pool을 활용한 Blocking I/O 오프로딩 전략
PR 링크: vllm-project/vllm#39763 상태: Merged | 변경: +None / -None
들어가며
고성능 추론 엔진인 vLLM 프로젝트에서 최근 흥미로운 성능 개선 작업이 진행되었습니다. 바로 Pooling 엔트리포인트(Embedding, Classification 등)의 전처리(Preprocessing) 및 후처리(Postprocessing) 로직을 Thread Pool로 오프로딩한 것입니다.
이 PR이 해결하고자 하는 핵심 문제는 비동기 토크나이저 도입 이후 발생한 약 2ms 수준의 지연 시간(Latency Regression)입니다. 비동기 프로그래밍 모델에서 CPU 집약적이거나 블로킹(Blocking) 성격이 있는 작업을 이벤트 루프에서 직접 실행할 경우, 루프가 차단되어 전체적인 처리 속도가 저하됩니다. 이번 변경사항은 이러한 작업을 별도의 스레드 풀로 넘겨 이벤트 루프의 반응성을 확보하는 데 중점을 두었습니다.
코드 분석: 무엇이 바뀌었나?
1. PoolingServing의 구조적 변화
가장 큰 변화는 vllm/entrypoints/pooling/base/serving.py에서 확인할 수 있습니다. 기존에는 async 함수 내에서 직접 전/후처리를 호출했으나, 이제는 Executor를 통해 비동기적으로 실행합니다.
Before:
async def __call__(
self,
request: AnyPoolingRequest,
raw_request: Request | None = None,
) -> Response:
ctx = await self._init_ctx(request, raw_request)
await self.io_processor.pre_process_online_async(ctx)
# ... 중략 ...
await self.io_processor.post_process_online_async(ctx)
return await self._build_response(ctx)
After:
def __init__(self, ...):
# ...
# 공유 스레드 풀 엑스큐터 설정
self._executor: Executor = models.renderer._executor
self._preprocessing_async = make_async(
self._preprocessing, executor=self._executor
)
self._postprocessing_async = make_async(
self._postprocessing, executor=self._executor
)
async def __call__(self, request, raw_request):
io_processor = self.get_io_processor(request)
ctx = await self._init_ctx(io_processor, request, raw_request)
# 스레드 풀에서 실행
await self._preprocessing_async(io_processor, ctx)
await self._prepare_generators(ctx)
await self._collect_batch(ctx)
# 후처리 및 응답 생성도 스레드 풀에서 실행
return await self._postprocessing_async(io_processor, ctx)
여기서 make_async는 동기 함수를 스레드 풀에서 실행하고 그 결과를 await 할 수 있게 만드는 헬퍼 함수입니다. 이를 통해 이벤트 루프를 차단하지 않고도 CPU 연산이 필요한 전처리를 수행할 수 있게 되었습니다.
2. torch.inference_mode()의 적용
단순히 스레드만 분리한 것이 아니라, 추론 전용 컨텍스트를 명시하여 오버헤드를 줄였습니다.
After:
@torch.inference_mode()
def _preprocessing(
self, io_processor: PoolingIOProcessor, ctx: PoolingServeContext
):
return io_processor.pre_process_online(ctx)
@torch.inference_mode()
def _postprocessing(
self, io_processor: PoolingIOProcessor, ctx: PoolingServeContext
):
io_processor.post_process_online(ctx)
return self._build_response(ctx)
@torch.inference_mode() 데코레이터는 그라디언트 계산 등에 필요한 메타데이터 기록을 비활성화하여 성능을 최적화합니다.
3. 메서드 네이밍 및 인터페이스 정리
IOProcessor 내부의 메서드 이름이 더 직관적으로 변경되었으며, 불필요한 async 래퍼 메서드들이 제거되었습니다.
Before:
def _preprocess_completion_online(self, ...):
# ...
async def pre_process_online_async(self, ctx: PoolingServeContext):
self.pre_process_online(ctx)
After:
def _preprocess_cmpl_online(self, ...): # 이름 축약 및 명확화
# ...
# 불필요한 _async 래퍼 제거 (Serving 레이어에서 처리)
왜 이게 좋은 최적화인가?
1. 지연 시간(Latency) 회복
PR 설명에 따르면, 비동기 토크나이저 도입 시 발생했던 2ms의 지연 시간 회귀(Regression)를 이 방식을 통해 완전히 해결했습니다. 벤치마크 결과, online 환경에서 기존 3.58ms 수준의 성능을 3.59ms로 유지하면서도 비동기 구조의 이점을 챙길 수 있었습니다.
2. 높은 동시성(High Concurrency)에서의 안정성
이벤트 루프가 전/후처리 연산에 묶여 있지 않기 때문에, 수많은 클라이언트가 동시에 요청을 보내는 상황에서 처리량(Throughput)이 더 안정적으로 유지됩니다. 특히 n_clients가 늘어날수록 스레드 풀을 통한 오프로딩의 효과는 극대화됩니다.
3. 비동기 렌더러의 함정 회피
리뷰어와의 논의에서 드러났듯이, 단순히 모든 것을 async 함수로 만드는 것이 정답은 아닙니다. tokenize_prompts_async와 같은 함수가 내부적으로 이벤트 루프를 비효율적으로 점유할 경우 오히려 성능이 저하될 수 있습니다. 이번 PR은 "무거운 동기 작업은 스레드 풀로, I/O 대기는 비동기로"라는 원칙을 충실히 따랐습니다.
요약 및 교훈
- 이벤트 루프 보호: 비동기 프레임워크(FastAPI, asyncio)에서 CPU 연산이 많은 작업은 반드시 별도의 스레드나 프로세스로 분리해야 합니다.
- 오버헤드 측정: 스레드 풀 사용에 따른 오버헤드는 현대적인 시스템에서 무시할 수 있는 수준(거의 0에 수렴)이며, 오히려 병목 현상 제거로 얻는 이득이 훨씬 큽니다.
- 적절한 추상화:
make_async와 같은 유틸리티를 사용하여 동기 로직을 비동기 흐름에 자연스럽게 통합하는 구조가 코드 유지보수 측면에서도 유리합니다.
vLLM과 같은 고성능 서빙 엔진에서는 단 1~2ms의 차이가 대규모 클러스터에서의 비용 절감으로 이어집니다. 이번 최적화는 파이썬 비동기 프로그래밍의 정석적인 개선 사례라고 볼 수 있습니다.
참고 자료
- https://pytorch.org/docs/stable/generated/torch.inference_mode.html
- https://docs.python.org/3/library/concurrent.futures.html#executor-objects
- https://fastapi.tiangolo.com/async/#path-operation-functions
⚠️ 알림: 이 분석은 AI가 실제 코드 diff를 기반으로 작성했습니다.
관련 포스트
PR Analysis 의 다른글
- 이전글 [triton] Triton 테스트 속도 혁신: Python 루프에서 벡터화된 NumPy로의 전환
- 현재글 : [vllm] vLLM 성능 최적화: Thread Pool을 활용한 Blocking I/O 오프로딩 전략
- 다음글 [vllm] vLLM, Qwen3-VL 비디오 추론을 위한 CUDA Graph 최적화: 성능 향상의 비결
댓글