2026. 1. 8. 17:52ㆍ논문리뷰
이번에 상용화된 코드로 사용하기 용이한 vLLM에 대한 리뷰입니다.
발표자료는 제가 논문 세미나를 위해 열심히 만든 자료입니다.
참고만 해주시면 감사하겠습니다.
틀린 부분도 있을 수 있습니다. 부족하지만 열심히 읽고 리뷰한 논문 재밌게 봐주세용.


vLLM의 기존 문제점 - 비효율적인 KV캐시 메모리 관리
LLM을 서빙할 때 높은 throughput을 달성하려면, 많은 요청을 한 번에 배치로 처리하는 것이 필수적입니다. 하지만 메모리의 제약이 성능에 큰 영향을 미칩니다.
전체 메모리에서 약 65%는 모델의 가중치(weights)로 고정된 크기를 차지하고, 약 30%는 KV 캐시(Key-Value Cache)가 차지합니다. 이때 중요한 점은, 모델 가중치는 고정된 크기이므로 메모리 크기를 줄일 수 없다는 점입니다. 따라서, 최대 배치 크기를 결정하는 중요한 요소는 바로 KV 캐시 관리입니다.
예시: OPT 13B 모델 (130억 파라미터)
- 한 토큰(단어 하나)의 KV 캐시 크기는 약 800KB입니다.
- 한 문장(2048개의 토큰)의 KV 캐시 크기는 약 1.6GB입니다.
즉, 한 번의 질문에만 1.6GB의 메모리가 필요합니다. 만약 GPU 메모리가 80GB라면, 한 번에 처리할 수 있는 요청의 수는 약 40~50개 정도가 됩니다. 연산 속도는 빠르지만, 메모리 제약으로 인해 처리 속도가 느려지는 메모리 바운드 문제가 발생합니다.
LLM의 메모리 구조
- 약 65%는 서빙 중에 정적인(static) 모델 가중치
- 약 30%는 요청들의 동적인 상태들(Transformer 모델에서는 attention 메커니즘에 관련된 key와 value 텐서, 즉 KV 캐시)
- 나머지 부분은 임시 텐서들(activations), 즉 LLM을 평가할 때 생성되는 임시 데이터들
KV 캐시의 역할
LLM이 문장을 생성할 때는, 이전에 생성한 단어(토큰)의 정보를 계속 기억해야 합니다. 이를 위해 사용하는 것이 KV 캐시입니다.
- Key: 각 단어의 “위치 정보”
- Value: 각 단어의 “의미(은닉 상태)”
이 두 개의 데이터를 매번 저장해두어야, 다음 단어를 생성할 때 이를 참조할 수 있습니다. 그러나 이 KV 캐시가 매우 큰 메모리를 차지하는데, 예를 들어 OPT 13B 모델의 경우:
- 한 토큰의 KV 캐시 크기: 약 800KB
- 한 문장의 KV 캐시 크기 (2048 토큰 기준): 약 1.6GB
GPU 메모리와의 한계
따라서, 하나의 질문에만 1.6GB가 필요하므로, GPU 한 장(80GB 기준)에서는 동시에 처리할 수 있는 요청이 40~50개에 불과합니다.
GPU의 연산 속도는 날로 빨라지고 있지만, 메모리 용량은 거의 늘지 않기 때문에 이 메모리 문제는 LLM의 서빙 속도에 큰 제약을 둡니다.

기존 LLM 서빙 시스템에서의 메모리 비효율
기존의 LLM 서빙 시스템은 대부분 KV 캐시를 연속적인 메모리 공간에 저장하는 방식을 사용합니다. 이는 딥 러닝 프레임워크에서 텐서를 연속적인 메모리 공간에 저장하는 것이 일반적이기 때문인데요. 그러나 LLM의 특성상 출력 길이가 예측 불가능하고 동적으로 변화하기 때문에, 이 방식은 몇 가지 주요한 메모리 낭비 문제를 초래합니다.
1. 내부 메모리 단편화 (Internal Memory Fragmentation)
기존 시스템에서는 요청의 최대 길이에 해당하는 메모리를 미리 할당합니다. 하지만 실제 요청 길이가 최대 길이보다 짧을 경우, 할당된 메모리의 일부는 사용되지 않게 됩니다. 예를 들어, 요청 길이가 최대 길이의 절반만 필요한 경우, 나머지 절반의 메모리는 낭비됩니다. 이는 내부 메모리 단편화를 초래합니다.
2. 외부 메모리 단편화 (External Memory Fragmentation)
서로 다른 요청 길이 때문에 외부 메모리 단편화가 발생할 수 있습니다. 이는 메모리가 비효율적으로 분배되고, 새로운 요청을 처리하기 위한 공간을 찾는 데 어려움이 생겨, 결국 메모리 낭비를 초래합니다.
3. 메모리 예약으로 인한 비효율 (Inefficiency due to Reserved Memory)
각 요청에 대해 최대 길이에 해당하는 메모리를 예약하다 보면, 실제로 사용되지 않는 메모리가 발생합니다. 이 미사용 메모리는 다른 요청에서 활용할 수 없으므로, 전체 시스템의 메모리 활용도가 낮아지며, 결과적으로 배치 크기와 처리량에 제한을 두게 됩니다.
따라서, 기존 LLM 서빙 시스템에서는 메모리 비효율이 발생하는 주요 원인으로 내부/외부 단편화와 메모리 예약이 있으며, 이는 전체 시스템의 성능 저하로 이어집니다. 이 문제를 해결하기 위한 효율적인 메모리 관리가 필요합니다.

입력 및 출력 길이가 불확실한 경우의 스케줄링
LLM 서비스로 들어오는 요청들은 입력(prompt)과 출력(output) 길이가 제각각입니다. 이로 인해, 메모리 관리 시스템은 다양한 길이의 프롬프트를 처리할 수 있도록 유연해야 합니다.
또한, 디코딩이 진행되면서 출력 길이가 증가하면, 해당 요청의 KV 캐시가 차지하는 메모리도 함께 증가하는 문제에 직면하게 됩니다. 이 경우, 새로운 요청이 들어올 때 사용할 메모리가 부족해지거나, 이미 실행 중인 요청의 생성을 유지할 메모리가 고갈되는 상황이 발생할 수 있습니다.
이러한 문제를 해결하기 위해, 특정 요청의 KV 캐시를 삭제하거나, GPU 메모리에서 디스크(또는 CPU 메모리)로 스왑 아웃(swap-out)하는 방식으로 스케줄링 결정을 내려야 합니다. 즉, 실시간으로 캐시를 빼고 옮기는 메모리 스케줄링이 필요합니다.

PagedAttention 알고리즘과 vLLM 라이브러리 제안
이 작업에서는 PagedAttention이라는 새로운 어텐션 알고리즘을 제안하고, 이를 활용한 vLLM LLM 서비스 엔진을 구축하여, §3에서 언급된 문제들을 해결합니다. vLLM의 아키텍처는 그림 4에 제시되어 있습니다.
vLLM은 중앙 집중형 스케줄러(centralized scheduler)를 사용하여 여러 GPU 워커들의 실행을 조정합니다. KV 캐시 관리자(KV cache manager)는 PagedAttention을 활용하여 KV 캐시를 페이지(paged) 방식으로 효율적으로 관리합니다. 구체적으로, 중앙 스케줄러는 명령을 각 GPU 워커에 전달하고, KV 캐시 관리자는 그 명령을 받아 각 GPU 워커의 물리적 KV 캐시 메모리를 제어합니다.

PagedAttention: 운영체제의 페이징 개념에서 영감을 받은 새로운 어텐션 알고리즘
PagedAttention은 운영체제의 페이징(paging) 개념에서 영감을 받아 개발된 새로운 어텐션 알고리즘입니다. 기존의 어텐션 알고리즘에서는 Key와 Value 벡터를 반드시 연속된 메모리 공간에 저장해야 했습니다. 그러나 PagedAttention은 비연속(non-contiguous) 메모리 공간에서도 Key와 Value 벡터를 분할 저장할 수 있도록 설계되었습니다.
기존의 방식에서는 전체 토큰을 한 번에 계산하는 방식이었지만, PagedAttention은 이를 블록 단위로 나누어 어텐션 연산을 진행합니다. 각 블록은 고정된 개수의 토큰(Key/Value 쌍)을 포함하고, 이 블록들은 GPU 메모리의 어디에든 비연속적으로 저장될 수 있습니다.
연산이 시작될 때, 쿼리 q_i가 입력으로 들어오면, 각 블록의 Key k_j와 Value v_j를 하나씩 읽어와 곱셈 연산을 수행합니다. 그런 다음, 각 블록에서 계산된 결과를 합쳐서 최종 어텐션 출력 o_i를 생성합니다.

기존 방식과 vLLM의 차이점
기존 시스템
기존의 LLM 시스템은 요청을 처리할 때, KV 캐시를 연속된 메모리 공간에 미리 전체 할당하는 방식이었습니다.
예를 들어, “이 요청은 최대 2048 토큰까지 생성 가능하니까, 2048개의 자리 전부를 미리 확보하자!”는 방식입니다.
하지만 실제로 요청이 500 토큰만 쓰게 되면, 나머지 1500개의 공간은 비워둔 채로 메모리를 차지하게 되며, 이는 메모리 낭비를 초래합니다. 이 방식은 고정된 메모리 예약(Static Allocation) 방식으로, 사용되지 않는 메모리 공간을 예약해놓는 구조였습니다.
vLLM 방식: 논리 ↔ 물리 메모리 분리
vLLM은 기존 방식과 다릅니다. vLLM은 요청이 들어오면 논리적으로 2048개의 위치를 정의해두고, 물리적으로는 처음 몇 개의 블록만 할당합니다. 이를 통해 메모리 할당의 유연성을 확보할 수 있습니다.
- 논리 블록: 이 블록은 “이 요청이 나중에 사용할 수 있는 가상 공간”입니다.
- 물리 블록: 이는 “현재 실제로 사용되고 있는 공간”입니다.
이 두 블록은 블록 테이블(Block Table)을 통해 매핑(mapping)됩니다. 새로운 토큰이 생성될 때마다, 논리 블록 중 남은 공간에 새 물리 블록이 동적으로 할당됩니다. 이 방식은 운영체제(OS)에서 가상 페이지가 실제 메모리 페이지에 매핑되는 방식과 매우 유사합니다.
vLLM의 메모리 관리
vLLM의 핵심 아이디어는 운영체제(OS)의 가상 메모리 개념을 LLM의 KV 캐시 관리에 적용한 것입니다. 운영체제에서는 메모리를 고정 크기의 페이지(pages)로 나누고, 논리적 페이지(logical page)를 물리적 페이지(physical page)에 매핑하여 비연속적인 공간에 저장하더라도 프로그램에서 연속된 메모리처럼 접근할 수 있게 합니다.
vLLM은 이를 KV 캐시에 적용하여, PagedAttention 알고리즘을 통해 고정 크기의 KV 블록(KV blocks)을 생성합니다. 하나의 요청에 대한 KV 캐시는 논리적 KV 블록(logical KV blocks)들의 연속으로 표현되며, 새로운 토큰과 그에 대응하는 KV 캐시가 생성될 때마다 이 블록들이 왼쪽에서 오른쪽으로 채워집니다. 아직 채워지지 않은 블록의 공간은 미래의 토큰 생성을 위해 예약됩니다.
GPU 워커에서는 블록 엔진(block engine)을 통해 GPU 메모리 내에 연속된 공간을 확보하고, 이를 물리적 KV 블록으로 나눕니다. 이 과정은 CPU RAM에도 동일하게 적용되어, 스왑 시 활용될 수 있습니다.
KV 블록 관리자(KV block manager)는 각 요청의 논리 블록 ↔ 물리 블록 매핑 관계를 저장하는 블록 테이블을 유지합니다. 이 테이블에는 각 논리 블록이 연결된 물리 블록의 정보와 현재 채워진 토큰 개수가 기록됩니다.
이와 같은 논리 블록과 물리 블록의 분리 덕분에, vLLM은 모든 토큰에 대해 미리 메모리를 예약하지 않고도 KV 캐시를 동적으로 확장할 수 있습니다. 이 구조는 기존 시스템에서 발생했던 메모리 낭비를 제거하며, 효율적인 메모리 관리를 가능하게 합니다.

① 초기 단계 (프롬프트 처리 / 메모리 예약)
OS의 가상 메모리와 마찬가지로, vLLM은 처음에 가능한 최대 생성 시퀀스 길이에 대한 메모리를 예약할 필요가 없습니다. 대신, 프롬프트 계산 중 생성되는 KV 캐시를 수용할 수 있는 필요한 KV 블록만 예약합니다.
프리필(Prefill) 단계
프리필 단계에서는 전통적인 self-attention 알고리즘을 사용하여, 프롬프트의 KV 캐시와 첫 번째 출력 토큰의 KV 캐시를 생성합니다.
이때,
- 처음 4개의 토큰의 KV 캐시는 논리 블록 0에 저장됩니다.
- 그 다음 3개의 토큰의 KV 캐시는 논리 블록 1에 저장됩니다.
논리 블록 1의 남은 한 칸은, 이후 생성될 자가회귀(autoregressive) 단계에서 사용될 공간으로 예약됩니다.

2. 첫 번째 자기 회귀 디코딩 단계
첫 번째 자기 회귀 디코딩 단계에서 vLLM은 PagedAttention 알고리즘을 사용하여 물리적 블록 7과 1에서 새 토큰을 생성합니다. 이때 마지막 논리적 블록에는 한 개의 슬롯이 남아 있으므로, 새로 생성된 KV 캐시는 그 슬롯에 저장됩니다. 이후, 블록 테이블에서 #filled 레코드가 업데이트됩니다.
3. 두 번째 디코딩 단계
두 번째 디코딩 단계에서는, 마지막 논리적 블록이 가득 차 있기 때문에, vLLM은 새로 생성된 KV 캐시를 새로운 논리적 블록에 저장합니다. 이를 위해 새로운 물리적 블록(물리적 블록 3)을 할당하고, 이 매핑 정보를 블록 테이블에 기록합니다.
디코딩 과정
디코딩 과정에서 vLLM은 각 디코딩 반복(iteration)마다 배치용 후보 시퀀스 세트를 선택하고(§4.5에서 자세히 설명), 새로운 논리적 블록에 대해 물리적 블록을 할당합니다. 그런 후, vLLM은 현재 반복에서 필요한 모든 입력 토큰(프롬프트 단계 요청 및 생성 단계 요청)을 하나의 시퀀스로 결합하여 LLM에 입력합니다. LLM의 계산 중, vLLM은 PagedAttention 커널을 사용하여 논리적 KV 블록 형식으로 저장된 이전 KV 캐시를 액세스하고, 새로 생성된 KV 캐시는 물리적 KV 블록에 저장됩니다.
블록 크기와 메모리 사용
KV 블록 내에 여러 개의 토큰을 저장하는 방식(block size > 1)은 PagedAttention 커널이 더 많은 위치에서 KV 캐시를 병렬로 처리할 수 있게 하여 하드웨어 활용도를 높이고 지연 시간을 줄이는 효과를 가져옵니다. 그러나 블록 크기가 커질수록 메모리 단편화가 증가할 수 있습니다.
물리적 블록 할당
vLLM은 새로 생성된 토큰과 KV 캐시가 생길 때마다 동적으로 새로운 물리적 블록을 논리적 블록에 할당합니다. 모든 블록은 왼쪽에서 오른쪽으로 채워지며, 새로운 물리적 블록은 이전 블록이 모두 가득 찬 후에만 할당됩니다. 이 방식은 요청에 대한 모든 메모리 낭비를 하나의 블록 내로 제한하여 메모리 효율성을 극대화하고, 결과적으로 더 많은 요청을 효율적으로 처리할 수 있게 해 줍니다.
요청 완료 후 메모리 관리
한 요청이 완료되면, 해당 요청의 KV 블록은 다른 요청들의 KV 캐시를 저장하기 위해 해제될 수 있습니다.
그림에서는 두 개의 시퀀스에 대한 메모리 관리 예시를 보여줍니다. 두 시퀀스의 논리적 블록은 GPU 작업자의 블록 엔진에 의해 예약된 공간 내에서 서로 다른 물리적 블록에 매핑됩니다. 이웃한 논리적 블록들은 물리적 GPU 메모리에서 반드시 인접할 필요가 없으며, 물리적 블록의 공간은 두 시퀀스가 효율적으로 공유할 수 있습니다.

두 개의 출력(샘플)을 동시에 디코딩하는 상황을 살펴보겠습니다. 두 샘플은 동일한 입력 프롬프트를 사용하므로, 프롬프트 단계에서는 프롬프트 상태(prompt state)를 한 번만 저장하면 됩니다.
- 즉, 두 시퀀스의 논리 블록 0, 1은 각각 물리 블록 7, 1에 매핑됩니다. 이때, 동일한 물리 블록이 여러 논리 블록에서 공유되는 상태가 됩니다.
- 하나의 물리 블록이 여러 논리 블록에 공유될 수 있기 때문에, 각 물리 블록은 참조 횟수(reference count)를 가집니다. (이 예시에서는 블록 7과 블록 1의 참조 횟수가 각각 2입니다.)
생성 단계에서의 Copy-on-Write
생성(Generation) 단계로 넘어가면, 두 샘플 A1과 A2가 각각 다른 토큰을 생성하게 됩니다. 이 시점부터는 KV 캐시를 별도로 저장해야 합니다.
이때 vLLM은 운영체제에서 사용하는 Copy-on-Write(COW) 메커니즘을 활용합니다. 이 메커니즘은 "읽을 때는 공유하지만, 수정하려면 복사해서 별도 공간에 쓴다"는 원리입니다.
구체적인 동작은 다음과 같습니다:
- 샘플 A1이 자기 논리 블록(논리 블록 1)에 새로운 KV 캐시를 쓰려고 할 때, vLLM은 해당 물리 블록(물리 블록 1)의 참조 횟수가 1보다 크다는 것을 확인합니다. (즉, 다른 샘플도 이 블록을 사용하고 있다는 뜻입니다.)
- 그럼으로써 vLLM은 새로운 물리 블록(물리 블록 3)을 할당하고, 블록 1의 내용을 복사(copy)하여 참조 횟수를 1 줄입니다.
- 그 후, 샘플 A2가 블록 1에 쓸 때는 참조 횟수가 이미 1이므로, 복사 없이 직접 수정하여 블록 1에 덮어씁니다.

Beam Search와 vLLM의 KV 블록 관리
Beam Search 개요
LLM 작업에서 기계 번역과 같은 경우, 사용자는 모델이 출력하는 가장 적합한 상위 k개의 번역을 기대합니다. 이를 실현하기 위한 알고리즘이 바로 Beam Search입니다. Beam search는 LLM에서 가장 가능성 높은 출력 시퀀스를 디코딩하는 데 널리 사용됩니다. 이 알고리즘은 샘플 공간을 완전히 탐색하는 계산 복잡성을 완화하는 데 도움을 줍니다.
Beam search는 beam width라는 매개변수 k에 의존하여, 디코딩 중에 상위 k개의 후보 시퀀스를 유지합니다. 각 단계에서 후보 시퀀스를 확장하고, 가능한 모든 토큰을 고려한 후, LLM을 통해 해당 확률을 계산합니다. 그 후, k · |V| 후보 중 상위 k개의 가장 확률 높은 시퀀스만을 유지합니다. 여기서 |V|는 어휘 크기입니다.
vLLM과 Beam Search의 KV 블록 관리
Beam Search와 vLLM의 주요 차이점 중 하나는 병렬 디코딩과 BLM 후보들 간 KV 캐시 공유의 방식입니다. vLLM은 초기 프롬프트 블록뿐만 아니라 다른 블록들도 후보들 간에 공유하며, 디코딩 과정이 진행됨에 따라 공유 패턴이 동적으로 변화합니다. 이 점은 운영체제(OS)에서 복합 분기(fork)에 의해 생성되는 프로세스 트리와 유사합니다.
그림 9는 k=4인 Beam Search 예제에서 vLLM이 KV 블록을 어떻게 관리하는지 보여줍니다. 예를 들어, 점선 이전에는 각 후보 시퀀스가 4개의 전체 논리 블록을 사용하고 있으며, 모든 beam 후보는 첫 번째 블록인 블록 0(즉, 프롬프트)을 공유합니다. 이 후, 각 후보는 디코딩이 진행됨에 따라 다르게 변화합니다. 후보 3은 두 번째 블록에서 다른 후보들과 차이를 보이며, 후보 0-2는 첫 번째 3개의 블록을 공유하고, 네 번째 블록에서 갈라집니다.
물리적 블록 해제와 참조 카운트
디코딩 반복 중에 상위 4개의 후보가 계속해서 선택됩니다. 이때, 후보 1과 2는 더 가능성 있는 후보로 선정되며, 후보 0과 3은 더 이상 상위 후보에 포함되지 않게 됩니다. 이 경우, 더 이상 사용되지 않는 논리 블록은 해제되고, 해당 물리 블록의 참조 카운트가 감소합니다.
vLLM은 참조 카운트가 0인 물리 블록을 해제하여 메모리 낭비를 줄입니다. 예를 들어, 블록 2, 4, 5, 8은 더 이상 사용되지 않으므로 해제됩니다. 이후에는 새로운 후보들의 새 KV 캐시를 저장하기 위해 새로운 물리 블록(블록 9-12)을 할당합니다.
메모리 복사 오버헤드의 최적화
기존의 LLM 서비스 시스템에서는 beam 후보들 간에 KV 캐시를 자주 복사해야 했습니다. 예를 들어, 그림 9에서 점선 이후에 후보 3은 후보 2의 KV 캐시를 복사하여 생성을 계속해야 했습니다. 이때 발생하는 빈번한 메모리 복사 오버헤드는 vLLM의 물리적 블록 공유로 크게 줄어듭니다.
vLLM에서는 서로 다른 beam 후보들이 대부분의 블록을 공유할 수 있습니다. 새로 생성된 토큰이 이전에 공유된 블록 내에 있을 때만 Copy-on-Write(COW) 메커니즘이 적용됩니다. 즉, 단 하나의 블록 데이터만 복사하는 방식으로, 메모리 사용을 최소화하며 효율적인 디코딩을 지원합니다.

vLLM에서 요청 트래픽 처리 및 블록 퇴출 정책
요청 트래픽 처리
vLLM은 시스템의 용량을 초과하는 요청 트래픽이 발생할 경우, 요청의 하위 집합을 우선 처리해야 합니다. 이를 위해 vLLM은 선착순 처리(FCFS, First-Come-First-Serve) 스케줄링 정책을 채택하여 공정성을 보장하고 기아 상태를 방지합니다. 이 정책에 따라, vLLM은 요청을 처리할 때 먼저 도착한 요청을 우선 처리합니다. 또한, 최신 요청은 우선적으로 선점됩니다.
LLM 서비스에서는 입력 프롬프트의 길이가 크게 달라질 수 있고, 그로 인해 생성되는 출력 길이도 예측할 수 없다는 특이한 도전에 직면해 있습니다. 입력과 출력이 증가하면서, vLLM은 새로운 KV 캐시를 저장할 GPU의 물리적 블록이 부족해질 수 있습니다.
두 가지 주요 질문: 블록 퇴출과 복구
이런 상황에서 vLLM은 두 가지 주요 질문에 대해 해결책을 마련해야 합니다:
- 어떤 블록을 퇴출시킬 것인가?
- 퇴출된 블록을 다시 필요로 할 때 어떻게 복구할 것인가?
일반적으로, 퇴출 정책은 어떤 블록이 가장 오래 사용되지 않았는지 예측하여 해당 블록을 퇴출시키는 휴리스틱을 사용합니다. 그러나 vLLM의 경우, 하나의 시퀀스 내 모든 블록이 함께 접근되므로, 모두 또는 아무것도 퇴출하지 않는 정책을 구현합니다. 즉, 하나의 시퀀스에 속하는 모든 블록을 함께 퇴출하거나 전혀 퇴출하지 않는 방식입니다.
또한, 하나의 요청 내에서 여러 시퀀스(예: 빔 서치 요청에 대한 빔 후보들)는 시퀀스 그룹으로 함께 스케줄링됩니다. 이 시퀀스 그룹 내의 시퀀스들은 메모리 공유 가능성 때문에 항상 함께 선점되거나 재스케줄링됩니다.
퇴출된 블록의 복구 방법
퇴출된 블록을 어떻게 복구할 것인지에 대해 vLLM은 두 가지 기술을 고려하고 있습니다. 이를 통해 퇴출된 블록을 효율적으로 복구하여, 메모리 활용도를 높이고 시스템 성능을 유지할 수 있습니다.

어떤 블록(요청)을 제거할지 (Eviction Policy)
일반적으로 운영체제(OS)에서는 LRU(Least Recently Used)나 LFU(Least Frequently Used)와 같은 휴리스틱을 사용하여 가장 나중에 사용될 것 같은 데이터를 예측하고, 이를 하나씩 내보내는 방식을 사용합니다. 그러나 LLM의 KV 캐시는 시퀀스 전체가 하나의 덩어리로 쓰이기 때문에, 일부만 제거하는 방식은 적합하지 않습니다. 즉, 시퀀스의 일부 블록만 제거하면 불완전한 상태가 발생할 수 있어, 전체 시퀀스를 처리해야 합니다.
All-or-Nothing Eviction
이 문제를 해결하기 위해 vLLM에서는 All-or-Nothing Eviction 방식을 채택합니다. 이 방식은 하나의 문장(시퀀스)에 해당하는 KV 캐시를 항상 통째로 관리하여, 시퀀스에 포함된 모든 블록을 함께 제거하는 방법입니다.
이 방식은 시퀀스의 일관성을 유지하면서도, 메모리 관리를 효율적으로 할 수 있도록 돕습니다. 시퀀스 내의 어떤 블록이든 한 번에 제거될 수 있도록 하여, 부분적으로 블록을 제거하는 것보다 간단하고 효과적인 방법을 제공합니다.

vLLM의 스와핑(Swapping)과 재계산(Recomputation) 전략
스와핑(Swapping)
스와핑(Swapping)은 대부분의 가상 메모리 시스템에서 사용하는 전형적인 기법으로, 퇴출된 페이지를 디스크의 스왑 공간으로 복사하는 방식입니다. vLLM에서는 퇴출된 블록을 CPU 메모리로 복사하는 방식으로 스와핑을 처리합니다.
vLLM은 GPU 블록 할당자 외에도 CPU RAM에 퇴출된 블록을 관리하는 CPU 블록 할당자를 포함하여 메모리 관리합니다. 만약 새로운 토큰을 위한 물리적 블록이 부족해지면, vLLM은 일련의 시퀀스를 퇴출시키고 그들의 KV 캐시를 CPU로 전송합니다. 이 후, 시퀀스를 선점하고 블록을 퇴출한 뒤, vLLM은 모든 선점된 시퀀스가 완료될 때까지 새로운 요청을 받지 않습니다. 요청이 완료되면, 해당 요청의 블록은 메모리에서 해제되고, 선점된 시퀀스의 블록이 다시 GPU 메모리로 불러와져 그 시퀀스의 처리가 계속됩니다.
이 설계에서 중요한 점은 CPU RAM으로 스와핑된 블록 수가 GPU RAM의 전체 물리적 블록 수를 초과하지 않도록 되어 있다는 점입니다. 즉, CPU RAM의 스왑 공간은 GPU 메모리에서 할당된 공간에 의해 제한되며, 이를 통해 메모리 낭비를 방지합니다.
재계산(Recomputation)
재계산(Recomputation)은 선점된 시퀀스를 재스케줄링할 때, KV 캐시를 단순히 재계산하는 방식입니다. 이 방식은 대기시간을 크게 줄일 수 있습니다. 이유는 디코딩 중 생성된 토큰들이 원래의 사용자 프롬프트와 결합되어 새로운 프롬프트로 처리될 수 있기 때문입니다. 이때, 모든 위치의 KV 캐시를 한 번의 프롬프트 단계 반복에서 생성할 수 있어, 기존의 대기시간보다 빠르게 처리됩니다.
재계산은 vLLM이 메모리 공간을 보다 효율적으로 관리하고, 동시에 빠른 디코딩을 가능하게 하는 중요한 전략입니다.

아래는 제가 참고한 자료에 대한 url입니다.
논문 paper: https://arxiv.org/abs/2309.06180
vLLM page: https://docs.vllm.ai/en/latest/
참고 자료(vLLM): https://mz-moonzoo.tistory.com/116
참고 자료(vLLM): https://dwin.tistory.com/161