혼자면 나쁘고 둘이면 좋은 설정

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 쓸 만한 거야?”라는 내 질문의 답이 측정 도구 버그 하나에 걸려 있었다.

배운 점