LLM의 Prefill과 Decode — 체감 속도를 좌우하는 두 단계

LLM의 Prefill과 Decode — 체감 속도를 좌우하는 두 단계
Photo by Nahrizul Kadri / Unsplash

LLM 추론은 크게 PrefillDecode 두 단계로 나뉜다.

사용자 프롬프트
      ↓
   Tokenizer
      ↓
┌──────────────┐
│   PREFILL    │
│ 전체 입력 처리 │
└──────────────┘
      ↓
   KV Cache
      ↓
첫 번째 토큰
      ↓
┌──────────────┐
│    DECODE    │
│ 한 토큰씩 생성 │
└──────────────┘
      ↓
최종 답변

핵심은 다음과 같다.

Prefill = 입력을 읽고 이해할 준비를 하는 단계
Decode = 실제 답변을 한 토큰씩 생성하는 단계

Prefill에서 실제로 무슨 일이 일어나는가

예를 들어 다음과 같은 질문을 넣는다고 하자.

대한민국의 수도는 어디인가?

Tokenizer를 거치면 여러 개의 token으로 변환된다.

[대한민국] [의] [수도] [는] [어디] [인가] [?]

이 토큰들을 Transformer의 모든 layer에 통과시킨다.

Input tokens
     ↓
Transformer Layer 1
     ↓
Transformer Layer 2
     ↓
...
     ↓
Transformer Layer N
     ↓
KV Cache 생성

각 layer에서 Attention과 MLP 계산을 수행하고, Attention에서 사용되는 **K(Key)와 V(Value)**를 KV cache에 저장한다.

이렇게 해서 모델이 첫 번째 답변 토큰을 생성할 준비가 끝난다.

이 과정이 Prefill이다.

Decode는 무엇인가

Prefill이 끝나면 모델은 첫 번째 토큰을 생성한다.

예를 들어:

대한민국의 수도는 어디인가?

              ↓

              서울

다음 토큰을 생성할 때는 기존의 KV cache를 활용하면서 새로운 토큰만 계산한다.

기존 KV Cache
      +
   "서울"
      ↓
Transformer
      ↓
   "입니다"

다시:

KV Cache
   +
"입니다"
   ↓
Transformer
   ↓
"."

이 과정을 계속 반복한다.

즉 Decode는 본질적으로

한 번에 한 토큰씩 autoregressive하게 생성하는 과정이다.

Prefill과 Decode는 GPU 입장에서 성격이 다르다

이 부분이 중요하다.

Prefill

예를 들어 10,000개의 입력 token을 처리한다면:

10,000 tokens
      ↓
한꺼번에 대규모 행렬 연산
      ↓
GPU를 매우 높은 효율로 사용

대량의 행렬 연산을 병렬로 수행할 수 있기 때문에 상당히 빠르다.

Decode

반면 답변을 생성할 때는:

token 1
  ↓
계산

token 2
  ↓
계산

token 3
  ↓
계산

처럼 한 번에 하나씩 진행된다.

따라서 GPU의 거대한 연산능력을 충분히 활용하기 어렵다.

특히 Decode에서는 KV cache를 계속 GPU 메모리에서 읽고 쓰는 과정과 memory bandwidth가 중요한 병목이 된다.

그래서 일반적으로:

Prefill 속도 >>> Decode 속도

가 된다.

그래서 LLM 속도를 두 개로 봐야 한다

LLM 벤치마크에서 다음 두 숫자를 구분해야 한다.

Prompt processing speed
        ↓
     Prefill

Text generation speed
        ↓
      Decode

예를 들어:

Prefill : 2,000 token/s
Decode  :    40 token/s

라고 할 수 있다.

이 경우 입력을 읽는 속도는 초당 2,000 token인데 실제 답변 생성은 초당 40 token이라는 의미다.

TTFT(Time To First Token)

사용자가 실제로 느끼는 속도에서 중요한 것이 TTFT다.

TTFT는

요청을 보낸 순간부터 첫 번째 답변 token이 나올 때까지 걸리는 시간

이다.

예를 들어:

20,000 token context
       ↓
    Prefill
       ↓
      10초
       ↓
첫 번째 token
       ↓
Decode 40 tok/s

이면 답변 생성 자체는 40 tok/s로 빠르더라도 사용자는 처음 10초 동안 아무것도 안 보게 된다.

따라서 Agent에서는 TTFT가 굉장히 중요하다.

긴 Context에서는 Prefill이 특히 중요하다

OpenClaw이나 Codex 같은 Agent에서는 한 번의 질문에 들어가는 context가 상당히 길어질 수 있다.

예를 들어:

System prompt        3,000 tokens
Tools                2,000
Conversation history 5,000
Memory               3,000
Code context         7,000
User request           500
────────────────────────────
Total                20,500 tokens

사용자가 실제로 새로 입력한 것은 500 token뿐이지만,

모델은 20,500 token을 먼저 처리해야 한다.

따라서 Agent에서는 단순히

"이 모델은 40 tok/s다"

라고 비교하는 것만으로는 부족하다.

오히려

20K context를 얼마나 빨리 Prefill하는가?

가 체감속도에 상당히 중요하다.

로컬 LLM의 대략적인 Prefill / Decode 속도

정확한 숫자는 모델, quantization, context length, backend, GPU 등에 따라 크게 달라진다.

대략적인 감각은 다음 정도다.

환경 Prefill Decode
CPU only 50~500 tok/s 3~15 tok/s
Apple Silicon 500~3,000 tok/s 15~60 tok/s
RTX 3060/4060급 500~2,000 tok/s 20~60 tok/s
RTX 3080 Ti 1,000~4,000+ tok/s 20~60 tok/s
RTX 4090 2,000~6,000+ tok/s 30~100+ tok/s
RTX 5090급 3,000~10,000+ tok/s 50~150+ tok/s

단, 이 표의 숫자는 특정 모델의 보장값이 아니라 대략적인 범위다.

특히 Prefill은 context length에 따라 크게 달라진다.

27~30B 모델에서는 어떻게 되는가

RTX 3080 Ti 12GB에서 27~30B급 모델을 돌린다고 생각하면 훨씬 현실적인 숫자는 다음과 같은 범위다.

27B Q4 정도

Prefill : 약 700~1,500 tok/s
Decode  : 약 15~35 tok/s

30B급 MoE

Prefill : 약 600~1,500 tok/s
Decode  : 약 20~40 tok/s

7~9B급

Prefill : 약 2,000~4,000+ tok/s
Decode  : 약 50~100+ tok/s

실제 숫자는 backend와 offload 방식에 따라 크게 달라진다.

특히 3080 Ti는 VRAM이 12GB이므로 27~30B 모델에서는 CPU offload 여부가 성능에 큰 영향을 준다.

Ollama는 실제 추론 엔진인가?

엄밀하게 말하면 Ollama 자체가 핵심 inference engine은 아니다.

구조를 단순화하면:

Ollama
   ↓
Inference backend
   ↓
CUDA / Metal 등
   ↓
GPU

따라서 같은 모델, 같은 quantization, 같은 GPU라면 Ollama를 사용한다고 해서 본질적으로 Prefill이 절반으로 떨어지는 식의 차이가 생기는 것은 아니다.

Ollama의 장점은 모델 관리와 API 서버 등을 매우 쉽게 제공한다는 것이다.

llama.cpp

llama.cpp는 로컬 LLM 추론에서 매우 중요한 backend다.

구조는 대략:

llama.cpp
    ↓
CUDA
    ↓
NVIDIA GPU

또는 Apple Silicon에서는:

llama.cpp
    ↓
Metal
    ↓
Apple GPU

같은 방식으로 동작한다.

llama.cpp benchmark에서는 보통 다음처럼 구분한다.

pp = prompt processing
tg = text generation

즉:

pp → Prefill
tg → Decode

vLLM은 무엇이 다른가

vLLM은 특히 여러 요청을 동시에 처리하는 서버 환경에 강하다.

핵심 기술 중 하나가 continuous batching이다.

예를 들어 여러 사용자가 동시에 요청하면:

User A → Decode
User B → Prefill
User C → Decode
User D → Prefill

같은 요청들을 GPU에서 효율적으로 묶어 처리할 수 있다.

또한 KV cache 관리와 batching을 적극적으로 최적화한다.

따라서:

Ollama / llama.cpp
        ↓
개인용 단일 추론
        ↓
간단하고 편리


vLLM
        ↓
다중 요청
        ↓
Batching / KV cache 최적화
        ↓
서버 처리량 ↑

이라는 차이가 있다.

Prefix Caching이 매우 중요하다

OpenClaw/Codex 같은 Agent에서는 Prefill 자체보다 Prefix Cache가 더 중요해질 수도 있다.

예를 들어 첫 번째 요청이:

System prompt
+
Tools
+
Memory
+
Conversation
+
User request

이고 총 20,000 token이라고 하자.

첫 요청에서는:

20,000 token
      ↓
Prefill
      ↓
KV Cache 저장

한다.

그런데 두 번째 요청에서 앞부분 19,800 token이 그대로라면 굳이 처음부터 다시 계산할 필요가 없다.

기존 19,800 token
       ↓
기존 KV Cache 재사용

새로운 200 token
       ↓
추가 Prefill

       ↓

Decode

이렇게 할 수 있다.

따라서 긴 context를 반복적으로 사용하는 Agent에서는 Prefix/KV cache 재사용 여부가 체감속도에 엄청난 영향을 준다.

Context가 길어지면 Prefill도 느려진다

Prefill 속도를 하나의 숫자로 비교하면 안 되는 이유다.

예를 들어 어떤 환경에서:

512 tokens    → 1,000 tok/s
1K tokens     →   980 tok/s
2K tokens     →   900 tok/s
4K tokens     →   750 tok/s
8K tokens     →   600 tok/s
16K tokens    →   400 tok/s
32K tokens    →   250 tok/s

같은 식으로 떨어질 수 있다.

따라서

"이 모델 Prefill이 1,500 tok/s"

라는 말만으로는 의미가 부족하다.

PP512인지, PP4K인지, PP16K인지를 같이 봐야 한다.

LLM 추론을 가장 간단하게 이해하는 방법

                 INPUT
                   │
                   ▼
             ┌───────────┐
             │  PREFILL  │
             │ 전체 입력 처리 │
             └───────────┘
                   │
                   ▼
              KV CACHE
                   │
                   ▼
             첫 번째 token
                   │
                   ▼
             ┌───────────┐
             │   DECODE  │
             │ 한 토큰씩 생성 │
             └───────────┘
                   │
                   ▼
                 OUTPUT

각 단계의 주요 병목은 다르다.

단계 핵심 병목 중요한 요소
Prefill GPU 연산 / Matrix multiplication GPU 연산성능, batch
Decode Memory bandwidth / KV cache VRAM bandwidth, 모델 크기
긴 Context Context 처리량 Prefill speed
Agent 반복되는 Context Prefix/KV cache
다중 사용자 Batch 처리 Continuous batching

OpenClaw/Codex에서 특히 중요한 순서

27~35B급 로컬 모델을 OpenClaw이나 Codex에서 사용할 경우에는 단순 Decode TPS보다 다음 순서로 보는 것이 좋다.

① Prefix/KV Cache 재사용
          ↓
② 긴 Context에서 Prefill 속도
          ↓
③ TTFT
          ↓
④ Decode tok/s
          ↓
⑤ VRAM 사용량 / CPU offload

예를 들어 두 모델이 있다고 하자.

Model A
Prefill : 800 tok/s
Decode  : 40 tok/s

Model B
Prefill : 1,500 tok/s
Decode  : 30 tok/s

짧은 질문만 계속한다면 A가 더 빠르게 느껴질 수도 있다.

하지만 OpenClaw에서 20K context를 계속 재처리한다면 B가 첫 응답이 훨씬 빨리 시작되기 때문에 전체적인 체감속도가 더 좋을 가능성이 높다.

결국 로컬 Agent에서는

"몇 tok/s인가?"보다 "긴 context를 얼마나 빨리 처리하고, 얼마나 많이 재사용하는가?"

가 더 중요한 질문이 된다.

mastodon