혼자면 나쁘고 둘이면 좋은 설정
chunked prefill만 줄이면 꼬리 지연이 오히려 나빠지고, priority 스케줄링과 함께 쓰면 최악 TTFT가 51.8초에서 2.0초로 내려갔다. 커뮤니티 권고 네 가지를 실측으로 기각한 밤
DGX Spark의 vLLM 서버는 긴 공시 문서 처리와 짧은 채팅 요청을 같은 엔진에서 받는다. 동시 요청 처리량은 잘 나오는데, 긴 문서가 돌고 있을 때 들어온 짧은 요청은 첫 토큰이 나오기까지 50초 넘게 기다려야 했다. 커뮤니티가 권하는 설정들을 하나씩 켜 가며 이 꼬리 지연을 잡아 보려 했다.
하려던 것
8월 16일 밤에 동시성 벤치마크로 기준선을 잡았다. 동시 16요청에서 283t/s, 단일 대비 12배로 잘 늘어났다. 문제는 혼합 부하였다. 긴 공시 문서 처리가 돌고 있을 때 짧은 채팅 요청을 던지면 첫 토큰이 51.8초 뒤에 나왔다. 처리량은 좋은데 꼬리 지연이 나빴다.
자정을 넘기며 튜닝을 밀어붙였다. 지시는 두 가지였다. 커뮤니티에서 권하는 설정을 실측으로 확인할 것, 그리고 클라이언트가 뭘 지켜야 하는지까지 가이드로 남길 것.
어떻게 시켰나
권고를 하나씩 켜고 같은 벤치를 돌렸다. 결과가 전부 예상과 달랐다.
| 설정 | 출처 | 실측 | 판정 |
|---|---|---|---|
--max-num-seqs 4 | 공식 블로그 권고 | 동시 16에서 12배 스케일링, 절벽 없음 | 기각 |
--max-num-batched-tokens 8192 단독 | 커뮤니티 | 최악 TTFT 51.8초 → 65.4초로 악화 | 단독 기각 |
--kv-cache-dtype fp8 | 커뮤니티 | 500토큰 이후 같은 문장 반복 루프 | 기각 |
| ngram 투기 디코딩 | 커뮤니티 | 요약 +12%, 자유 서술 −36% | 기각 |
max-num-seqs 4는 단일 사용자 저지연 환경의 권고였다. 우리는 배치 서버다. 전제가 다른데 그 전제는 권고문에 안 적혀 있었다.
어디서 막혔나
프리필 청크를 8192로 줄이면 긴 문서가 잘게 쪼개져 짧은 요청이 끼어들 틈이 생길 줄 알았다. 실제로는 더 나빠졌다. 스케줄러가 선착순이라 틈이 생겨도 짧은 요청은 여전히 뒤에 서 있고, 청크를 쪼갠 오버헤드만 얹혔다.
priority 스케줄링을 켜도 마찬가지였다. 클라이언트가 priority 필드를 안 보내면 최악 TTFT가 69.6초로 기준선보다 나빴다. Claude Code가 이 대목을 정확히 짚었다. “서버 설정만으로는 효과가 0이다.”
어떻게 풀었나
둘을 같이 켜고 클라이언트가 우선순위를 보내게 하니 최악 TTFT가 2.0초로 내려왔다. 청크는 끼어들 틈을 만들고, priority는 그 틈에 누가 들어갈지 정한다. 하나만 있으면 틈이 있어도 순서가 안 바뀌고, 순서를 정해도 틈이 없다. 처리량 손실은 0이었다. 294.8t/s로 기준선과 같았다.
아침에 투기 디코딩을 마저 봤다. ngram은 기각했고, 모델에 내장된 MTP 헤드는 실워크로드에서 2.2배가 나왔다. 요약이 17.9t/s에서 39.8t/s로 올랐다.
여기서 하나 더 걸렸다. 벤치 스크립트가 투기 디코딩의 청크 수를 토큰 수로 세고 있었다. 실제 39.5t/s가 14.3t/s로 찍혔다. MTP가 별로라고 결론 낼 뻔한 걸 원본 응답을 세어 보고 잡았다. “그래서 MTP 쓸 만한 거야?”라는 내 질문의 답이 측정 도구 버그 하나에 걸려 있었다.
배운 점
- 권고에는 전제가 붙어 있고 보통 안 적혀 있다. 단일 사용자용 권고를 배치 서버에 적용하면 반대 결과가 나온다. 내 부하로 다시 잰다.
- 설정은 조합으로 잰다. 청크 단독은 악화, priority 단독은 악화, 둘이 함께면 25배 개선이었다. 하나씩 켜고 끄는 실험은 조합 효과를 놓친다.
- 서버 설정 중 일부는 클라이언트 계약이다. priority는 클라이언트가 필드를 보내야 작동한다. 가이드 문서에 “보내야 하는 것”을 같이 적는다.
- 투기 디코딩은 종류마다 다르다. ngram은 자유 서술에서 손해, 모델 내장 MTP는 전 구간 이득이었다.
- 결과가 이상하면 측정 도구를 먼저 의심한다. 14.3t/s는 모델이 아니라 벤치 스크립트가 낸 숫자였다.