prefill은 연산, decode는 메모리 — LLM 추론 병목은 하나가 아니다

prefill은 연산, decode는 메모리 — LLM 추론 병목은 하나가 아니다

⚡ 3줄 요약

  • LLM 추론은 하나의 작업이 아니다 — prefill은 연산 병목, decode는 메모리 대역폭 병목이라 처방이 정반대다.
  • “2배 빠르다” 같은 벤치마크는 하드웨어·배치·캐시 조건과 세트다. 숫자만 떼어오면 거짓말이 된다.
  • 최적화 순서는 정해져 있다 — 구간을 나눠 측정 → 병목 구간만 손대기. 양자화·배치를 먼저 지르는 건 순서가 틀렸다.

LLM 추론 최적화의 첫 단추는 기법이 아니라 측정이다 — prefill과 decode의 병목이 정반대라, 어디가 막히는지부터 재야 한다.모델을 더 작게 만들면 추론이 빨라진다.” 절반만 맞는 말이다. LLM 추론은 성격이 완전히 다른 두 단계로 쪼개진다. 입력 프롬프트를 한꺼번에 읽는 prefill과, 토큰을 한 개씩 뱉는 decode다. 이 둘은 병목이 정반대라서, 한쪽에 잘 듣는 처방이 다른 쪽엔 아무 효과가 없거나 오히려 손해다. 그래서 최적화의 첫 단추는 튜닝이 아니라 측정이다. 어디가 막히는지 모르고 양자화부터 지르면 돈만 쓰고 체감이 없다.

prefill과 decode는 병목이 정반대다

prefill은 입력 전체를 병렬로 처리한다. 행렬 대 행렬 곱(GEMM) 위주라 연산 밀도가 높고, H100 기준 GPU 사용률이 90%를 넘길 만큼 연산(compute) 병목이다. 반면 decode는 토큰 하나를 만들 때마다 KV캐시 전체를 GPU 메모리에서 다시 읽어온다. 행렬 대 벡터 곱(GEMV)이라 연산량은 적은데 메모리를 계속 왕복해야 해서, 속도를 결정하는 건 연산력이 아니라 메모리 대역폭이다. 이 구간의 GPU 사용률은 20~40%까지 떨어진다. 컨텍스트가 길수록 이 왕복이 커지는데, 어텐션이 128K에서 추론 시간의 85%를 먹어치우는 것도 같은 결의 문제다.

정리하면 이렇다. prefill이 느리면 GPU 연산이 부족한 것이고, decode가 느리면 메모리 대역폭이 부족한 것이다. 최근 서빙 시스템이 두 단계를 아예 다른 하드웨어로 나누는(prefill-decode disaggregation) 이유가 여기 있다. 원인이 다르니 자원도 따로 배정하는 게 맞다.

단계 연산 성격 진짜 병목 H100 GPU 사용률(참고)
prefill (입력 처리) 행렬×행렬(GEMM) 연산(compute) 90~95%
decode (출력 생성) 행렬×벡터(GEMV) 메모리 대역폭 20~40%

여기에 요청이 실행 전 대기열에 머무는 queueing, 여러 요청을 묶어 GPU에 태우는 batching까지 더하면 병목은 네 갈래가 된다. 특히 에이전트 작업은 긴 입력에 도구 호출 결과, 여러 단계 출력이 뒤섞여 들어가므로 이 네 구간을 반드시 따로 재야 한다. 애초에 Transformer가 긴 입력을 어떻게 우회하도록 설계됐는지를 알면 prefill 부담이 왜 입력 길이에 민감한지도 보인다.

⚠️ 처리량과 지연시간은 같이 안 움직인다

처리량(token/s)은 단위 시간에 처리한 토큰 양, 지연시간(latency)은 한 요청이 답을 받기까지 걸린 시간이다. 배치를 키우면 처리량은 올라가지만 개별 요청은 대기열에서 더 기다려 지연시간이 나빠질 수 있다. 둘은 자주 반대로 움직인다. 하나만 보고 “빨라졌다”고 판단하면 틀린다.

LLM 추론에서 prefill·decode·queueing·batching 구간을 나눠 비교한 도해
추론 병목은 하나가 아니라 구간별로 나눠 봐야 한다

양자화·배치는 ‘병목 맞춤 처방’이지 만능이 아니다

흔히 쓰는 최적화 기법도 어느 구간을 겨냥하는지가 다르다. 무턱대고 다 적용하는 게 아니라, 앞에서 잰 병목에 맞춰 골라야 한다.

기법 주로 듣는 구간 기대 효과(참고 범위)
FP8 양자화 메모리·대역폭 H100에서 FP16 대비 처리량 약 1.5~2배, VRAM 약 절반
연속 배칭(continuous batching) batching·처리량 vLLM·TensorRT-LLM에서 처리량 3~5배 개선 사례
KV캐시 압축·오프로딩 decode·메모리 더 긴 컨텍스트·더 큰 배치 여유 확보

주의할 함정이 있다. INT8 양자화는 모델이 작아지는데도 배치 크기 1에서는 FP16보다 느려질 수 있다. 곱셈 직전에 매번 FP16으로 되돌리는 비용이 메모리 절약분을 넘어서기 때문이다. “작게 만들면 무조건 빨라진다”가 성립하지 않는 대표 사례다. 기법마다 조건이 붙는다.

“2배 빠르다”는 조건과 세트로만 참이다

구체적인 사례가 이 점을 잘 보여준다. Together AI의 ThunderAgent는 에이전트 추론을 개별 요청이 아니라 스케줄 가능한 프로그램으로 다뤄 KV캐시 경합을 줄이는 스케줄러다. 코딩 에이전트 수백 개가 샌드박스와 수십 턴씩 동시에 주고받는 합성 데이터 생성 파이프라인에서 측정됐다. 공식 블로그가 제시한 단일 노드 수치는 아래와 같다.

항목 기준(SGLang) ThunderAgent 읽는 법
하드웨어 단일 8×H100 노드 + HiCache 장비가 바뀌면 재현값도 바뀐다
배치 크기 192 요청량·배치에 따라 결과가 달라진다
처리량 390 token/s 803 token/s 약 2.06배 (공식 “2배 이상”)
평균 지연시간 65s 10.6s 평균값이며 전 요청 보장 아님

여기서 두 가지를 바로잡아야 한다. 첫째, 처리량 개선은 803÷390 ≈ 2.06배다. Together AI도 “단일 노드 처리량 2배 이상, 높은 동시성에서 P50 지연시간 약 10배 감소”라고 못박는다. “2.5배” 같은 어림수를 떼어 쓰면 원문과 어긋난다. 둘째, 이 수치는 8×H100·HiCache·batch 192라는 조건과 한 세트다. 조건을 지우고 배율만 인용하는 순간 그 숫자는 사실상 무의미해진다.

ThunderAgent가 에이전트 추론 파이프라인의 병목을 줄이는 처리 흐름도
에이전트 추론 파이프라인에서 최적화가 개입하는 지점

🎯 FEEDON의 선택 — 우리라면 이렇게 한다

우리라면 튜닝 기법 목록부터 펼치지 않는다. 먼저 prefill·decode·queueing을 따로 계측해 어디서 시간이 새는지부터 확인한다. decode가 느리면 KV캐시·양자화, prefill이 느리면 연산 자원, 대기열이 길면 배치·스케줄링 — 병목에 맞는 처방만 골라 쓴다.

그리고 벤치마크 숫자는 항상 조건과 함께 기록한다. 하드웨어·런타임·캐시·배치를 고정하지 않은 배율은 마케팅 문구일 뿐 도입 근거가 못 된다. 남의 벤치마크가 아니라 내 요청 분포로 재현해야 진짜 판단이다.

추론 최적화 체크리스트

  • 대표 작업과 입력·출력 길이를 고정한다 (에이전트면 도구 호출까지 포함)
  • prefill·decode·queueing을 각각 계측한다 — 뭉뚱그리지 않는다
  • 하드웨어·모델·런타임·캐시·배치 설정을 전부 기록한다
  • 평균 latency만 보지 말고 p95·p99와 처리량을 함께 본다
  • 최적화 전후로 같은 요청 집합을 재생해 비교한다
  • 특정 조건의 결과를 “일반 성능 보장”으로 옮겨 쓰지 않는다

출처: Together AI — ThunderAgent 공식 블로그, prefill/decode 병목 및 양자화 관련 공개 자료 · 수치는 원문 실험 조건(8×H100·HiCache·batch 192)의 값이며 환경에 따라 달라짐

이 글은 FEEDON이 공개 1차 자료를 바탕으로 작성·검증했습니다.

✍️ FEEDON

FEEDON은 AI와 개발 도구의 변화를 1차 자료에서 직접 읽고 검증해 전한다. 연구 블로그·릴리스 노트·API 문서에서 시작해, 실무에 영향이 큰 변화만 골라 다룬다. "모델이 무엇을 할 수 있나"보다 "그게 당신의 비용과 워크플로에 무슨 뜻인가"를 먼저 따진다. 본문의 모든 수치는 공개 원문과 대조한 뒤 발행한다.