vLLM Internalised: The Mechanics of Modern LLM Inference
Quick Summary
vLLM은 KV 캐시를 작은 페이지로 관리하고 스케줄러가 토큰 예산을 배분함으로써, 단일 사용자 중심의 llama.cpp와 달리 GPU 위에서 다수의 동시 요청을 처리하도록 설계된 LLM 추론 엔진이다.
🖼️ 인포그래픽
🖼️ 4컷 인포그래픽
💡 한 줄 요약
vLLM은 KV 캐시를 작은 페이지로 관리하고 스케줄러가 토큰 예산을 배분함으로써, 단일 사용자 중심의 llama.cpp와 달리 GPU 위에서 다수의 동시 요청을 처리하도록 설계된 LLM 추론 엔진이다.
📌 핵심 요약
- 글은 llama.cpp와 vLLM을 대비하며, llama.cpp는 거의 모든 하드웨어에서 단일 사용자 실험에 적합하지만 높은 동시성 처리를 핵심 목표로 삼지는 않는다고 설명한다.
- vLLM의 핵심 차별점은 KV 캐시를 운영체제의 가상 메모리처럼 작은 비연속 페이지로 나누어 관리해, 고정된 연속 메모리 할당에서 생기는 낭비를 줄이는 데 있다.
- vLLM v1 구조의 Scheduler는 매 GPU 단계마다 어떤 요청이 다음 토큰을 생성할지 결정하는 ‘트래픽 컨트롤러’로, max_num_scheduled_tokens 기반의 token_budget 안에서 요청을 배치한다.
- 요청이 들어오면 add_request가 request_id 중복 여부와 스트리밍 상태를 확인하고, 새 요청은 waiting 큐에 넣어 이후 스케줄러와 메모리 매니저의 관리 대상으로 만든다.
- 글은 vLLM을 배우는 일이 특정 도구 사용법을 넘어서, 고동시성 LLM 서빙 엔진들이 공통으로 맞닥뜨리는 KV 캐시·스케줄링·GPU 처리 문제를 이해하는 길이라고 정리한다.
🧩 주요 포인트
- 글은 llama.cpp와 vLLM을 대비하며, llama.cpp는 거의 모든 하드웨어에서 단일 사용자 실험에 적합하지만 높은 동시성 처리를 핵심 목표로 삼지는 않는다고 설명한다.
- vLLM의 핵심 차별점은 KV 캐시를 운영체제의 가상 메모리처럼 작은 비연속 페이지로 나누어 관리해, 고정된 연속 메모리 할당에서 생기는 낭비를 줄이는 데 있다.
- vLLM v1 구조의 Scheduler는 매 GPU 단계마다 어떤 요청이 다음 토큰을 생성할지 결정하는 ‘트래픽 컨트롤러’로, max_num_scheduled_tokens 기반의 token_budget 안에서 요청을 배치한다.
- 요청이 들어오면 add_request가 request_id 중복 여부와 스트리밍 상태를 확인하고, 새 요청은 waiting 큐에 넣어 이후 스케줄러와 메모리 매니저의 관리 대상으로 만든다.
- 글은 vLLM을 배우는 일이 특정 도구 사용법을 넘어서, 고동시성 LLM 서빙 엔진들이 공통으로 맞닥뜨리는 KV 캐시·스케줄링·GPU 처리 문제를 이해하는 길이라고 정리한다.
🧠 상세 정리
1. llama.cpp와 vLLM의 출발점 차이
글은 먼저 이전에 다룬 llama.cpp를 기준점으로 삼아 vLLM의 역할을 설명한다. llama.cpp는 민첩하고 거의 모든 하드웨어에서 동작하며, 한 명의 사용자가 모델을 탐색하거나 실험하기에 적합한 도구로 묘사된다. 반면 높은 동시성을 처리하는 데 초점을 두지는 않으며, 단순히 사용자가 목적지에 도달하도록 돕는 쪽에 가깝다. vLLM은 같은 GPU 인프라라는 선로 위에서 수백 명의 동시 사용자를 함께 이동시키기 위해 설계된 시스템으로 제시된다. 따라서 글의 문제의식은 단순히 모델을 실행하는 것이 아니라, 많은 요청을 동시에 안정적으로 처리하는 추론 엔진의 내부 구조를 이해하는 데 있다.
2. KV 캐시를 페이지처럼 다루는 메모리 설계
원문은 llama.cpp에서 KV 캐시가 대체로 연속된 큰 메모리 블록으로 미리 할당되는 방식의 한계를 설명한다. KV 캐시는 대화의 단기 기억에 해당하므로 긴 문맥을 처리하는 데 필요하지만, 대화가 일찍 끝나거나 컨텍스트 창을 다 채우지 않으면 할당된 GPU 메모리가 낭비된다. vLLM은 이 문제를 운영체제의 가상 메모리와 유사한 방식으로 접근한다. KV 캐시를 작고 비연속적인 페이지로 나누어 관리함으로써, 하나의 큰 연속 공간을 확보해야 하는 부담을 줄인다. 이 구조 덕분에 같은 GPU 위에서 더 많은 대화를 동시에 수용할 수 있다는 것이 글의 핵심 설명이다.
3. scheduler.py와 토큰 예산의 의미
vLLM v1 구조에서 Scheduler는 매 GPU 단계마다 어떤 요청이 다음 토큰을 생성할지 결정하는 트래픽 컨트롤러로 설명된다. 스케줄러는 먼저 token_budget을 확인하며, 이는 해당 단계에서 GPU가 처리할 수 있는 토큰 수를 나타낸다. 요청이 100개 대기 중이더라도 모두 한꺼번에 처리하지 않고, max_num_scheduled_tokens라는 한계 안에서 가능한 만큼만 채운다. 만약 전체 일시정지 상태라면 token_budget은 0이 되어 아무 요청도 처리되지 않는다. 이 구조는 vLLM의 성능이 단순한 요청 수가 아니라, 각 단계에서 허용된 토큰 예산을 어떻게 배분하느냐에 크게 좌우된다는 점을 보여준다.
4. 초과 요청과 지연의 절벽
원문은 preempted_reqs 변수를 통해 스케줄러가 용량 초과 상황을 어떻게 다루는지 설명한다. token_budget이 소진되면, 아직 응답할 준비가 된 챗봇 요청이 있더라도 엔진은 해당 요청을 기다리게 만든다. 공간이 부족한 요청은 preempted_reqs 목록으로 이동하며, 이는 일부 세션을 잠시 멈추고 다른 요청을 우선 처리한다는 의미로 해석된다. 글은 이 때문에 vLLM 시스템에서 지연 시간이 일정 수준까지는 비교적 평평하게 유지되다가, token_budget 한계를 넘는 순간 급격히 악화될 수 있다고 설명한다. 즉 고동시성 추론의 병목은 점진적으로만 나타나는 것이 아니라, 용량 한계에 도달했을 때 절벽처럼 드러난다.
5. allocate_slots와 작은 페이지 단위의 실행 흐름
스케줄러 내부 루프는 실행 중인 요청을 순회하면서 token_budget이 남아 있는 동안 각 요청에 필요한 토큰 처리를 준비한다. 이때 vLLM은 큰 RAM 덩어리를 찾는 대신, kv_cache_manager.allocate_slots를 통해 필요한 만큼의 작은 블록을 추가로 확보할 수 있는지 묻는다. allocate_slots가 새 블록을 반환하면 해당 요청은 계속 진행할 수 있고, 반환하지 못하면 메모리 할당이 병목이 된다. 원문은 이 과정을 통해 vLLM의 성능이 KV 캐시 페이지 확보 능력과 직접 연결되어 있음을 보여준다. 또한 개별 프롬프트가 너무 길어 큰 컨텍스트 창을 요구하면, 스케줄러가 사용 가능한 블록을 찾는 일이 더 어려워질 수 있다고 설명한다.
6. 요청이 큐에 들어가 토큰이 생성되기까지
사용자 상호작용이 vLLM에 도착하면 add_request 메서드가 호출되고, 먼저 같은 request_id가 이미 존재하는지 확인한다. 이 확인은 스트리밍 응답을 처리하는 데 중요하며, 엔진이 새 질문과 기존 응답의 다음 조각을 구분할 수 있게 한다. 새 요청이라면 필요에 따라 streaming_queue를 준비하고, _enqueue_waiting_request를 통해 waiting 큐에 넣는다. 이 순간 사용자의 프롬프트는 단순한 API 입력에서 스케줄러가 관리하는 작업 단위로 바뀐다. 글은 전체 경로를 인터페이스 계층의 add_request, 스케줄러의 token_budget 대기, 메모리 매니저의 allocate_slots, GPU 실행에 따른 토큰 생성이라는 단계로 정리한다.
7. LLM 추론 엔진의 공통 구조로서 vLLM
마지막으로 원문은 vLLM을 특정 오픈소스 도구 하나로만 보지 말고, 현대 LLM 추론의 표준적인 구조를 이해하는 사례로 보자고 주장한다. 여러 상용 또는 관리형 LLM 서빙 엔진도 결국 GPU 위에서 트랜스포머를 효율적으로 서비스해야 한다는 같은 물리적 문제를 해결해야 한다. KV 캐시의 물리적 제약과 높은 동시 처리량의 필요성은 특정 벤더에 국한되지 않는 공통 조건이다. 그래서 주요 LLM 서빙 엔진들은 요청 스케줄링, 메모리 페이지화, GPU 실행 최적화라는 유사한 패턴으로 수렴한다고 글은 설명한다. vLLM을 배우는 것은 곧 고동시성 LLM 추론 시스템의 기본 모델을 배우는 일이라는 결론으로 이어진다.
🧾 핵심 주장 / 시사점
- vLLM의 핵심은 단순히 모델을 빠르게 실행하는 것이 아니라, 제한된 GPU 메모리와 토큰 처리량을 다수 사용자 사이에 어떻게 공정하고 효율적으로 배분하느냐에 있다.
- KV 캐시를 작은 페이지로 나누는 설계는 메모리 낭비를 줄이는 동시에, 긴 프롬프트와 높은 동시성이 만났을 때 병목이 어디서 발생하는지 이해하게 해준다.
- token_budget은 운영 관점에서 중요한 한계선이며, 이 값을 넘어서면 지연이 서서히 늘기보다 스케줄링 대기와 선점 때문에 급격히 악화될 수 있다.
✅ 액션 아이템
- vLLM의 단일 연속 KV 할당 방식과 페이지 기반 분할 캐시를 동일 조건에서 비교해 메모리 단편화 완화 폭을 수치화한다.
- add_request의 request_id 중복 검사와 스트리밍 상태 검증을 실제 요청 패턴으로 재현해 대기 큐 진입 동작을 점검한다.
- vLLM v1 스케줄러의 token_budget(max_num_scheduled_tokens) 적용 규칙을 동시성 부하별로 실측해 처리량·지연 트레이드오프 기준을 정한다.
❓ 열린 질문
- llama.cpp 대비 vLLM의 페이지 기반 KV 캐시 분할은 실제 다중 사용자 워크로드에서 메모리 낭비를 어느 수준까지 줄일 수 있는가?
- token_budget을 고정 운용할지 동적 조정할지 결정할 때 공정성, 지연, 처리량은 어떤 순위 기준으로 판단할 것인가?
- 요청 수락 단계의 request_id 중복 필터와 스트리밍 상태 검증이 큐 대기 정책에 미치는 영향은 무엇으로 정량화해 검증할 것인가?