LLM의 추론 과정은 크게 두 단계로 나뉩니다.

  1. Prefill(프리필) 단계 이 단계에서는 사용자가 입력한 프롬프트(문장)를 처음으로 처리합니다. 입력된 모든 토큰을 한 번에 병렬로 연산하여 첫 번째 출력 토큰을 생성하며, 다음 단계를 위해 중간 계산 결과인 Key-Value Cache도 만듭니다. 프리필 단계는 연산량이 많아 GPU 자원을 많이 소모하게 됩니다.

  2. Decode(디코딩) 단계 첫 번째 토큰이 생성된 후에는, 다음 토큰들을 순차적으로 하나씩 예측하게 됩니다. 이 과정에서는 이전에 생성된 토큰과 KV 캐시를 참고하여 다음 토큰을 만들어냅니다. 디코딩 단계는 주로 메모리 대역폭에 의존하는 특징이 있습니다.

PagedAttention: KV Cache 문제의 해결사

전통적인 방식에서는 각 시퀀스의 KV Cache가 GPU 메모리에서 **연속된 공간(contiguous space)**에 할당되어야 했습니다. 이는 다음과 같은 문제를 낳았습니다.

  • 내부 단편화 (Internal Fragmentation): 미리 큰 공간을 할당해두고 일부만 사용하여 메모리가 낭비됩니다.
  • 외부 단편화 (External Fragmentation): 사용 가능한 메모리 총량은 충분하지만, 긴 시퀀스가 들어갈 만한 ‘연속된’ 큰 공간이 없어 요청을 처리하지 못합니다.

vLLM의 PagedAttention은 운영체제의 ‘가상 메모리 페이징’ 기법에서 영감을 받아 이 문제를 해결합니다.

  • KV Cache를 고정된 크기의 ‘블록(block)’ 단위로 나눕니다.
  • 하나의 시퀀스에 대한 KV Cache는 물리적으로 떨어져 있는 여러 블록에 저장될 수 있습니다.
  • vLLM은 ‘블록 테이블’을 통해 어떤 블록이 어떤 시퀀스에 속하는지 관리합니다.

핵심 시사점: PagedAttention 덕분에 vLLM은 메모리 단편화 없이 거의 100%에 가까운 메모리 활용률을 달성할 수 있습니다.

A.1) Rerank 에서의 Point-wise vs. List-wise: PagedAttention 관점에서의 비교

PagedAttention이 있음에도 불구하고, 두 방식의 KV Cache 관리 방식은 시스템에 다른 종류의 부담을 줍니다.

1. Point-wise (병렬) 방식의 경우

  • 상황: 100개의 개별 요청이 시스템에 들어옵니다.

  • KV Cache 할당: vLLM은 100개의 독립적인 논리적 시퀀스(logical sequence) 를 생성하고, 각 시퀀스에 필요한 KV Cache 블록들을 할당합니다. 각 요청의 프롬프트(쿼리+아이템)가 짧아 시퀀스당 필요한 초기 블록 수는 적을 수 있습니다.

  • 숨겨진 비용과 한계:

    1. 시퀀스 개수 제한 (max_num_seqs): vLLM은 동시에 처리할 수 있는 최대 시퀀스 개수에 대한 제한이 있습니다. 만약 max_num_seqs가 64로 설정되어 있다면, 100개의 요청이 동시에 들어와도 최대 64개만 ‘실행 중(Running)’ 상태가 될 수 있고 나머지는 ‘대기(Pending)’ 상태가 됩니다. 이것이 바로 Latency를 급증시키는 큐 대기 시간의 직접적인 원인입니다.

    2. 메타데이터 오버헤드: 100개의 시퀀스를 관리하기 위해 100개의 블록 테이블과 관련 메타데이터를 유지해야 합니다. 이는 스케줄러에 상당한 부담을 주며, 스케줄링 루프 자체를 느리게 만들 수 있습니다.

    3. 메모리 비효율 가능성: 각 시퀀스가 아주 작은 양의 메모리만 필요로 할 때 (예: 블록 크기의 10%만 사용), 블록의 나머지 90%는 낭비됩니다. 100개의 시퀀스에서 이런 낭비가 누적될 수 있습니다. (내부 단편화와 유사한 문제)

2. List-wise (직렬) 방식의 경우

  • 상황: 100개 아이템 정보가 담긴 하나의 거대한 요청이 들어옵니다.

  • KV Cache 할당: vLLM은 단 하나의 논리적 시퀀스를 생성합니다. 이 하나의 시퀀스를 위해 매우 많은 수의 KV Cache 블록을 할당합니다.

  • 압도적인 효율성:

    1. 시퀀스 개수 문제 없음: 단 하나의 요청이므로 max_num_seqs 제한에 전혀 걸리지 않습니다. 시스템은 이 요청을 처리하면서도 다른 사용자들의 요청을 받을 여유가 충분합니다.

    2. 낮은 메타데이터 오버헤드: 관리할 시퀀스와 블록 테이블이 하나뿐이므로 스케줄러의 부담이 매우 적습니다.

    3. 높은 메모리 효율: 하나의 긴 시퀀스가 여러 블록을 거의 꽉 채워서 사용하므로, 블록 단위의 메모리 낭비가 훨씬 적습니다.

결론: 주차장 비유로 보는 진실

  • GPU 메모리 = 거대한 주차장
  • KV Cache 블록 = 주차 공간 한 칸
  • vLLM 스케줄러 = 주차 관리 요원
  • Point-wise 방식: 스쿠터 100대가 동시에 주차장에 들어오는 것과 같습니다. 각 스쿠터는 주차 공간을 하나만 차지하지만, 관리 요원은 100대의 스쿠터에 대해 일일이 출입을 기록하고, 주차권을 발급하고, 위치를 관리해야 합니다. 주차장 입구는 순식간에 아수라장이 되고, 뒤에 오는 차들은 하염없이 기다려야 합니다.
  • List-wise 방식: 매우 긴 굴절 버스 한 대가 들어오는 것과 같습니다. 이 버스는 주차 공간 20칸을 차지하지만, 관리 요원은 단 한 명의 버스 기사와 소통하고 주차권 한 장만 발급하면 됩니다. 버스가 차지할 20칸의 공간만 확보되면, 나머지 공간에는 다른 차들이 원활하게 들어올 수 있습니다.

이 비유처럼, Point-wise 방식의 진짜 문제는 KV Cache의 총량이 아니라, 너무 많은 개별 시퀀스를 동시에 관리해야 하는 ‘메타데이터 및 스케줄링 오버헤드’와 ‘최대 동시 시퀀스 개수 제한’ 에 있습니다. PagedAttention이 물리적 메모리 단편화를 해결해주었기 때문에, 병목 지점이 관리 오버헤드로 옮겨간 것입니다.

B) Benchmark

아래와 같은 결과를 얻을 수 있다.

============ Serving Benchmark Result ============
Successful requests:                     1000      
Benchmark duration (s):                  66.09     
Total input tokens:                      1024000   
Total generated tokens:                  127598    
Request throughput (req/s):              15.13     
Output token throughput (tok/s):         1930.60   
Total Token throughput (tok/s):          17424.08  
---------------Time to First Token----------------
Mean TTFT (ms):                          32598.97  
Median TTFT (ms):                        30023.49  
P99 TTFT (ms):                           60034.04  
-----Time per Output Token (excl. 1st token)------
Mean TPOT (ms):                          81.67     
Median TPOT (ms):                        81.43     
P99 TPOT (ms):                           121.57    
---------------Inter-token Latency----------------
Mean ITL (ms):                           81.27     
Median ITL (ms):                         38.47     
P99 ITL (ms):                            328.83    
----------------End-to-end Latency----------------
Mean E2EL (ms):                          37422.54  
Median E2EL (ms):                        44052.51  
P99 E2EL (ms):                           66713.44  
==================================================

B.1) 성능 지표 설명

B.1.1) Time to First Token (ttft)

  • 스트리밍 방식으로 데이터를 받아올 때, 첫 번째 토큰이 생성되기까지 걸리는 시간입니다.

B.1.2) Time per Output Token (tpot)

  • 첫 번째 토큰의 생성을 제외한 이후 토큰의 평균 생성 시간을 의미
  • itl 과 큰 차이는 없는데 일정 간격이라고 생각하면 좋을듯

B.1.3) Inter-token Latency (itl)

  • 이미 생성된 토큰 이후, 다음 토큰이 생성될 때까지의 간격을 의미합니다.

B.1.4) End-to-end Latency (etel)

  • 하나의 응답이 완전히 완료될 때까지, 즉 전체 토큰이 모두 생성되는 데 걸리는 시간을 나타냅니다.
  • 벤치마크 코드에서는 이 값을 latency 로 표현합니다.

C) Related

D) References