TECH 으로 돌아가기
TECH HACKER NEWS 오늘 8분 읽기 53 READS

vLLM 해부: 고성능 LLM 추론 시스템은 어떻게 처리량을 끌어올리나

vLLM 해부: 고성능 LLM 추론 시스템은 어떻게 처리량을 끌어올리나
SOURCE IMAGE · HACKER NEWS

대규모 언어모델을 실제 서비스로 운영할 때 GPU 비용과 응답 속도를 좌우하는 것은 모델 자체가 아니라 그 앞단의 추론 엔진이다. 아정 실무자라면 한 번쯤 이름을 들어봤을 vLLM은 페이지드 어텐션, 연속 배칭, 프리픽스 캐싱 같은 기법을 조합해 표준 트랜스포머를 고처리량으로 서빙하는 오픈소스 추론 시스템이다. 알렉사 고르디치가 정리한 vLLM 내부 해부 글은 이 시스템을 오프라인 엔진에서 시작해 비동기·다중 GPU·다중 노드 서빙으로 단계적으로 확장하며 설명한다. 이 글의 핵심 구성 요소와 그것이 실무에서 갖는 의미를 짚어본다.

엔진 코어와 페이지드 어텐션

vLLM의 가장 기본 단위는 LLM 엔진이다. 그 자체만으로도 높은 처리량을 낼 수 있지만, 이것만으로는 오프라인 배치 추론만 가능하고 웹을 통해 고객에게 서빙할 수는 없다. 엔진 코어 내부에서 특히 중요한 것이 KV 캐시 매니저다. 이 매니저는 사용 가능한 KV 캐시 블록의 풀인 free_block_queue를 관리하는데, 블록 수는 VRAM 용량과 블록 크기에 따라 수십만 개에 이르기도 한다. 페이지드 어텐션은 이 블록들을 인덱싱 구조로 삼아 각 토큰을 계산된 KV 캐시 블록에 매핑한다. 운영체제의 페이징처럼 메모리를 조각내어 관리함으로써 단편화를 줄이고 더 많은 요청을 동시에 담는 것이 핵심이다.

엔진은 요청을 검증해 투입한 뒤, 처리할 요청이 남아 있는 한 step() 함수를 반복 호출한다. 각 step은 세 단계로 이뤄진다. 동기 엔진 예제에서는 처음 투입된 프롬프트만 처리되고 실행 도중 새 요청을 끼워 넣을 방법이 없지만, 비동기 엔진은 매 step 이후 신규 요청과 기존 요청을 함께 고려한다. 이것이 연속 배칭이다.

스케줄러가 만드는 차이

추론 워크로드는 프롬프트를 한 번에 처리하는 프리필과 토큰을 하나씩 생성하는 디코드로 나뉜다. 두 작업은 성능 특성이 정반대여서 프리필은 연산 집약적(compute-bound)이고 디코드는 메모리 대역폭 집약적(memory-bandwidth-bound)이다. 과거 V0 엔진은 한 step에서 프리필이나 디코드 중 하나만 처리할 수 있었지만, V1 스케줄러는 더 영리한 설계 덕분에 두 종류를 같은 step에 섞어 처리한다. 스케줄러가 결정을 내리면 execute_model 호출이 Worker를 거쳐 모델 러너로 위임되며 실제 순전파가 수행된다.

청크 프리필은 긴 프롬프트의 프리필 단계를 작은 조각으로 쪼개는 기법이다. 이것이 없으면 아주 긴 요청 하나가 한 step을 독점해 다른 요청들의 대기 시간을 늘린다. 구현은 단순해서, step당 새 토큰 수를 long_prefill_token_threshold 값으로 상한을 두면 나머지는 기존 인덱싱 로직이 알아서 처리한다.

재사용과 제약으로 얻는 속도

프리픽스 캐싱은 여러 프롬프트가 앞부분에서 공유하는 토큰을 매번 다시 계산하지 않게 해준다. 기본값으로 KV 캐시 블록 하나는 16토큰이며, 그보다 긴 공통 접두부는 한 번만 계산해 KV를 저장해두고 이후 요청에서 재사용한다. 해시로 블록을 식별하고, find_longest_cache_hit로 이미 캐시된 블록을 찾아 참조 카운트를 관리하는 방식이다. 다만 이 이점은 프리필에만 적용되고 디코드 속도는 개선하지 못한다. 프리픽스 캐싱은 기본 활성화되어 있으며 enable_prefix_caching을 꺼서 비활성화할 수 있다.

출력 형식을 강제해야 하는 실무에서는 가이디드 디코딩이 유용하다. 매 디코딩 단계에서 문법 기반 유한상태기계(FSM)가 로짓을 제약해 허용된 토큰만 샘플링되도록 한다. 정규 문법(정규식 수준)부터 대부분의 프로그래밍 언어를 포괄하는 문맥자유문법까지 강제할 수 있다. 내부적으로는 xgrammar 같은 백엔드가 현재 FSM 상태에 맞는 비트마스크를 만들어 허용되지 않는 토큰의 로짓을 음의 무한대로 설정한다.

투기적 디코딩(speculative decoding)은 작은 드래프트 모델이 k개의 토큰을 저렴하게 제안하고 큰 모델이 수용·거부 규칙으로 검증하는 방식이다. 이 규칙 덕분에 결과 분포는 큰 모델에서 토큰별로 직접 샘플링한 것과 통계적으로 동등하면서도, 한 번의 대형 모델 순전파로 최대 k+1개 토큰을 얻어 속도를 높인다. 흥미롭게도 vLLM V1은 별도 드래프트 LM 방식은 지원하지 않고, 더 빠르지만 덜 정확한 n-gram, EAGLE, Medusa 방식을 채택했다.

대규모로 확장하기

모델 가중치가 단일 GPU VRAM에 들어가지 않으면 같은 노드 안에서 텐서 병렬화(예: TP=8)로 쪼개고, 그래도 부족하면 노드 간 파이프라인 병렬화로 넘어간다. 이때 여러 GPU 프로세스를 조율하는 것이 MultiProcExecutor이며, 엔진 입장에서는 execute_model 호출 뒤로 이 복잡성이 모두 추상화되어 인터페이스가 바뀌지 않는다. 더 나아가 데이터 병렬화(DP>1)로 모델을 여러 노드에 복제하고 로드 밸런싱과 API 서버를 앞단에 두어 스케일 아웃할 수 있다. 예컨대 H100 노드 두 대에서 헤드리스 모드로 엔진을 띄우면 DP 복제본마다 메인·입력·출력 스레드가 도는 자식 프로세스들이 코디네이터와 핸드셰이크를 마친 뒤 정상 상태 루프에 진입한다.

프리필과 디코드의 성능 특성이 다르다는 점은 P/D 분리(disaggregated prefill/decode)라는 설계로 이어진다. N개의 프리필 인스턴스와 M개의 디코드 인스턴스를 실시간 요청 구성에 맞춰 오토스케일링하고, 프리필 워커가 KV를 전용 캐시 서비스에 쓰면 디코드 워커가 이를 읽어간다. 이렇게 하면 길고 폭발적인 프리필 부하를 지연에 민감한 디코드와 격리해 TTFT(첫 토큰까지 시간)와 ITL(토큰 간 지연)을 더 세밀하게 통제할 수 있다. 결국 vLLM의 처리량은 하나의 묘책이 아니라 메모리 관리, 스케줄링, 캐싱, 병렬화 계층이 맞물려 나오는 결과이며, 자신의 워크로드가 프리필 위주인지 디코드 위주인지에 따라 어떤 기능을 켤지 판단하는 것이 실무의 출발점이다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://www.aleksagordic.com/blog/vllm
SHARE
처리 중...