투기 디코딩이 느려진 게 아니라 lookup이 안 맞았던 겁니다: 같은 모델에서 11.9배
긴 컨텍스트를 서빙하면서 투기 디코딩(speculative decoding)을 한 번 시험해 보고 “우리 워크로드에는 안 맞는다”로 접으셨다면, 그때 어느 방식을 쟀는지 확인해 볼 만합니다. 저희가 바로 그렇게 접었다가 다시 열어 보니, 문제는 투기 디코딩이 아니라 방식 선택이었습니다. 같은 모델, 같은 GPU에서 초당 15.0 토큰이 178.8 토큰이 됐습니다.
같은 이름 “투기 디코딩” 아래 서로 다른 후보 원천: 프롬프트 안에서 찾는 lookup과, 은닉 상태로 예측하는 드래프터.
처음 결론은 반쪽이었습니다
저희는 27B 모델을 NVFP4로 양자화해 긴 컨텍스트로 서빙합니다. 여기에 투기 디코딩을 얹어 보고 자유 서술 과제에서 기준선보다 느려지는 것을 확인했습니다. 수용 길이가 복사형 과제의 5.4~7.9에서 1.2~2.3으로 무너졌고, 거부된 초안의 검증 비용만 남았습니다. 그래서 “양자화가 이미 메모리 대역폭 병목을 없앴으니 투기 디코딩이 파고들 여지가 없다”고 정리했습니다.
이 설명은 그럴듯했고, 절반만 맞았습니다.
저희가 잰 것은 lookup 계열(suffix, ngram) 두 가지였습니다. 이 방식은 다음에 올 토큰의 후보를 프롬프트 안에서 찾습니다. 문서를 그대로 받아쓰는 과제에서는 정답이 눈앞에 있으니 잘 맞고, 요약이나 추론처럼 프롬프트에 없는 문장을 새로 쓸 때는 찾을 것이 없습니다. 수용 길이가 무너진 것은 모델 탓도 양자화 탓도 아니고 후보를 어디서 가져오는가의 문제였습니다.
학습된 드래프터는 다르게 동작합니다. 작은 모델이 타깃의 은닉 상태를 보고 다음 토큰을 예측하므로, 프롬프트에 한 번도 등장하지 않은 토큰도 맞힐 수 있습니다. 그래서 같은 조건에 DFlash2 드래프터를 넣어 측정했습니다.
은닉 상태"] --> B["드래프터 (작은 모델)
다음 K개 토큰 예측"] B --> C["타깃 모델이
K개 동시 검증"] C -->|모두 일치| D["K개 한 번에 수용"] C -->|첫 불일치 지점| E["일치한 접두만 유지
불일치 지점서 재생성"] D --> A E --> A
투기 디코딩의 핵심은 드래프터가 K개의 초안을 만들고, 그걸 타깃 모델이 한 번의 전향 계산으로 함께 검증하는 점입니다. lookup 계열은 이 드래프터 자리에 “프롬프트에서 후보를 검색한다”는 규칙을 얹을 뿐이고, 학습된 드래프터는 그 자리에 은닉 상태를 읽는 작은 모델을 넣는 것입니다. 같은 자리, 다른 후보 원천이 11.9배의 차이를 냅니다.
NotebookLM이 소스를 종합해 생성한 인포그래픽입니다.
11.9배
프롬프트 234,063 토큰, 워밍업 3회 뒤 5회 반복, 단일 B200, 엔드포인트 대 엔드포인트 비교입니다.
| 구성 | 출력 처리량 | 재실행 산포 |
|---|---|---|
| 현행 프로덕션 (vLLM v0.24.0, 드래프터 없음) | 15.0 tok/s | 1.00배 |
| 나이틀리 엔진 + DFlash2 (K=7) | 178.8 tok/s | 1.02배 |
재실행 산포가 양쪽 다 1.00~1.02배입니다. 잡을 여러 개 띄워 놓고 잰 값이 아니라 서빙 엔드포인트끼리 직접 비교한 값이라 경합이 없습니다.
이 11.9배는 두 레버가 겹친 결과입니다. 엔진을 v0.24.0에서 나이틀리로 바꾸는 것만으로 5.43배(15.0에서 81.4 tok/s)가 나오고, 드래프터가 그 위에 약 2.2배를 더합니다. 다만 81.4는 워밍업을 한 번만 준 조건에서 잰 값이라 내부 분해는 잠정치로 읽어 주시는 편이 정확합니다. 확정된 것은 현행 대비 11.9배입니다.

품질은 그대로입니다
투기 디코딩은 초안 토큰을 타깃 모델이 검증하고 틀리면 버리는 구조입니다. 그래서 출력 분포가 타깃 모델 단독과 같아야 하고, 이론상 무손실입니다. 다만 이건 알고리즘의 성질이지 특정 구현의 성질은 아니어서 직접 확인했습니다.
이미지와 체크포인트와 과제를 고정하고 드래프터만 켜고 끈 A/B에서, 지표 34개 중 유의한 차이가 하나도 없었습니다.
| 지표 | 드래프터 OFF | ON | 차이 | p |
|---|---|---|---|---|
| GSM8K strict | 0.5100 | 0.5017 | −0.83pp | 0.774 |
| HAERAE | 0.7617 | 0.7626 | +0.09pp | 0.971 |
| KoBEST | 0.6700 | 0.6715 | +0.15pp | 0.956 |
| belebele-ko | 0.9000 | 0.8983 | −0.17pp | 0.922 |
| IFEval prompt-strict | 0.3087 | 0.3068 | −0.19pp | 0.946 |
가장 작은 p값이 0.774입니다. 부호는 절반씩 갈리고 여섯 개 지표는 소수점까지 같은 값이 나왔습니다. 다만 이건 “구분되지 않는다”이지 “동일하다”의 증명은 아닙니다. 문항 600개 기준으로는 4pp 수준의 작은 차이까지 잡아내지는 못합니다.
245k를 넘으면 서버가 죽습니다
켜기 전에 이걸 먼저 보셔야 합니다. 컨텍스트 창을 100만 토큰으로 열어 둔 엔드포인트에 길이를 늘려 가며 요청을 보냈습니다.
| 프롬프트 | 결과 | 수용 길이 |
|---|---|---|
| 8,410 | 정상 | 1.46 |
| 66,968 | 정상 | 1.42 |
| 148,503 | 정상 | 1.45 |
| 244,689 | 정상 | 1.47 |
| 약 300,000 | HTTP 500, 엔진 프로세스 사망 | 측정 불가 |
로그에는 CUDA error: cudaErrorIllegalAddress가 찍혔습니다. 파드는 재시작되지만, 요청 하나가 엔드포인트를 통째로 내렸다는 사실은 남습니다. 100만 토큰을 받겠다고 광고한 창의 4분의 3이 지뢰밭이었던 셈입니다.
처방은 광고와 실제를 맞추는 것입니다. max_model_len을 245,760으로 낮추고 확인했더니 240,503 토큰은 정상으로 처리되고 그보다 큰 요청은 프로세스를 죽이는 대신 깨끗한 HTTP 400을 받습니다. 244,689와 30만 사이의 정확한 경계는 아직 좁히지 않았고, 캡은 검증된 지점 바로 위에 놓아 두었습니다.

대가는 지연이 아니라 용량입니다
드래프터를 붙이면 KV 캐시가 줄어듭니다. 같은 설정에서 풀이 1,808,112 토큰에서 1,354,786 토큰으로 25% 감소했습니다. 100만 토큰짜리 요청 기준으로 동시에 처리할 수 있는 수가 1.81에서 1.35로 내려갑니다.
긴 컨텍스트 서빙이라면 여기부터 계산하셔야 합니다. 드래프터가 가져가는 것은 시간이 아니라 자리입니다.

재현하실 때 걸릴 만한 것들
측정하면서 세 번 넘어졌습니다. 셋 다 결과 숫자를 크게 바꾸는 것이었습니다.
워밍업 한 번으로는 부족합니다. 드래프터는 첫 실제 생성 시점에 CUDA 그래프를 잡습니다. 그래서 1회차가 15.3초, 2회차부터 1.3초로 산포가 11.64배까지 벌어집니다. 워밍업을 3회로 늘리자 다섯 번 반복이 176.8에서 180.5 사이로 안정됐습니다. 이 조정을 안 했으면 실제보다 12배 낮은 값을 보고할 뻔했습니다.
속도 측정과 품질 측정을 한 노드에 섞으면 안 됩니다. 품질 평가는 몇 시간이 걸리든 정답률이 같지만, 속도는 정의상 시간이라 다른 잡이 올라오면 그대로 오염됩니다. 속도 측정 도중에 품질 벤치 세 개를 올려 노드를 가득 채웠더니 한 셀의 산포가 6.28배로 터졌습니다. 한산한 노드에서 다시 재니 1.02~1.09배로 정상화됐습니다.
반복을 늘려도 줄지 않는 산포는 노이즈가 아닙니다. 복사형 과제의 동시성 16 구간은 5회 반복에서 1.52배, 15회에서 1.55배로 좁혀지지 않았습니다. 표본을 세 배로 늘려도 그대로면 분포가 실제로 두 갈래라는 뜻이고, 그건 측정 문제가 아니라 운영에서 겪을 사실입니다. 같은 동시성에서 자유 서술은 1.05배로 안정적이니, 흔들리는 것은 동시성이 아니라 워크로드 쪽입니다.

ThakiCloud 관점
이 결과는 Metis(추론 플랫폼)의 서빙 기본값을 어떻게 잡을지를 바꿉니다. 저희가 배운 것은 투기 디코딩을 켜고 끄는 이분법이 아니라 어느 구간에서 켜는가입니다. 드래프터는 짧고 중간 길이 요청에서 품질 손실 없이 배수를 주고, 긴 요청 경로에서는 용량을 먹고 경계를 넘으면 서버를 내립니다. 그래서 길이에 따라 라우팅하는 쪽이 전면 적용보다 안전합니다.
같은 규율이 Paxis(에이전트 플랫폼)에도 적용됩니다. 에이전트는 배치를 채워 주지 않고 한 번에 한 스트림을 기다리므로, 총 처리량보다 이 구간의 응답 속도가 체감을 지배합니다. 다만 그 이득을 광고된 컨텍스트 창 전체에서 약속할 수는 없다는 것이 이번 측정의 결론이기도 합니다.
이 수치는 프리픽스 캐시가 채워진 상태의 디코딩 속도입니다. 매번 완전히 새로운 234k 프롬프트가 들어오면 프리필 비용이 다시 붙습니다. 저희 실제 트래픽은 시스템 컨텍스트가 고정이라 캐시가 잘 듣는 쪽에 가깝지만, 그 전제가 다른 환경에서는 배수가 줄어듭니다.
출처
- Fast Inference from Transformers via Speculative Decoding (arXiv:2211.17192)
- EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty (arXiv:2401.15077)
- vLLM 문서: Speculative Decoding
본문의 방식 분류(프롬프트 안에서 후보를 찾는 lookup 계열과, 은닉 상태를 읽는 학습된 드래프터)는 vLLM 문서의 방법 목록과 위 두 논문에서 확인했습니다. 모든 링크는 2026년 8월 24일 실제 호출로 검증했습니다.