RTX 5080 16GB: 128k 컨텍스트에서의 Qwen3.6 35B MoE — 56 tok/s, 그리고 MTP가 도움이 되지 않는 이유
요약
RTX 5080 16GB 환경에서 Qwen3.6 35B MoE 모델을 테스트한 결과, 128k 컨텍스트에서 MTP(Multi-Token Prediction)를 사용하면 오히려 성능이 저하되는 현상이 발견되었습니다. MTP 연산 버퍼가 차지하는 VRAM으로 인해 일부 MoE 전문가 레이어가 CPU로 밀려나면서 병목 현상이 발생하기 때문입니다. 반면, GPU 메모리에 완전히 들어가는 27B 모델의 경우에는 MTP가 성능 향상에 도움이 됩니다.
핵심 포인트
- 16GB VRAM 환경에서 35B MoE 모델 사용 시 MTP를 적용하면 속도가 약 23% 느려짐
- MTP 연산 버퍼(약 1.5GB)가 VRAM을 점유하여 MoE 전문가 레이어를 CPU로 오프로드하는 것이 병목의 원인
- 모델이 GPU 메모리에 완전히 수용 가능한 경우(예: 27B 모델)에는 MTP가 성능 향상에 효과적임
- 최적의 설정: 128k 컨텍스트에서 35B Q4_K_XL 모델 사용 시 MTP 미사용 및 `--fit-target 1536` 권장
MTP (Multi-Token Prediction)가 b9190 빌드에서 llama.cpp 메인라인에 막 병합되었습니다. u/WarthogConfident4039 님께 Qwen3.6 벤치마킹 결과물을 보여드리겠다고 약속했었죠. 세 가지 설정을 실제 코딩 에이전트(coding-agent)의 컨텍스트 길이(단순히 512 토큰이 아닌)에서 테스트했습니다. 주요 결과는 저를 놀라게 했습니다.
요약(TL;DR): 35B Q4_K_XL, MTP 미사용, --fit-target 1536, 131k 컨텍스트. 이것이 최종 설정입니다. 128k 컨텍스트에서 생성 속도는 56 tok/s, 프롬프트 처리(prompt processing) 속도는 1,584 tok/s를 기록했습니다. 128k 컨텍스트에서는 MTP가 도움이 되지 않으며, 두 경우 모두 동일한 속도로 수렴합니다. 복잡함을 피하세요. 만약 56k 컨텍스트로 충분하다면(또는 35B 모델이 들어가지 않는 12GB 그래픽 카드를 사용 중이라면) 27B IQ3 모델을 고려해 볼 가치가 있습니다.
설정 (The Configs)
| 설정 | 27B IQ3+MTP (A) | 35B Q4_K_XL+MTP (B) | 35B Q8_0+MTP (C) |
|---|---|---|---|
| 모델 | Qwen3.6-27B MTP-UD-IQ3_XXS | Qwen3.6-35B-A3B MTP-UD-Q4_K_XL | Qwen3.6-35B-A3B MTP-Q8_0 |
| ... | |||
| 모든 테스트 환경: RTX 5080 16GB, Ryzen 9 9950X, 128GB RAM, llama.cpp b9204 (메인라인). |
공통 MTP 플래그: -np 1 --fit on -fa on -t 20 --no-mmap --jinja -ctk q8_0 -ctv q8_0 --spec-type draft-mtp --spec-draft-n-max 2
결과 (Results)
속도 — MTP의 반전
MTP 사용 시 (mtp-bench, 9가지 프롬프트 유형)
| 지표 | 27B IQ3 | 35B Q4_K_XL | 35B Q8_0 |
|---|---|---|---|
| 평균 tok/s | 73 | 74 | 46 |
| ... |
놀라운 점: 35B는 MTP가 없을 때 더 빠릅니다
| 35B Q4_K_XL 설정 | --fit-target | MTP 사용 여부 | 평균 tok/s | 사용된 VRAM |
|---|---|---|---|---|
| 최적 (MTP 미사용) | 0 | No | 97 | 15,815 MiB |
| ... | ||||
| 16GB 환경에서 35B MoE의 경우 MTP를 사용하면 속도가 23% 느려집니다. 이유는 다음과 같습니다: |
- MTP는 MTP 연산 버퍼(compute buffer)를 위해 약 1.5 GB를 예약하도록
--fit-target 1536설정을 요구합니다. - 이 1.5 GB로 인해 약 3개의 MoE 전문가 레이어(expert layers)가 GPU에서 CPU로 밀려납니다.
- CPU에 할당된 전문가 레이어는 MoE 추론(inference)의 병목 지점(bottleneck)이 됩니다.
- MTP의 멀티 토큰 투기(multi-token speculation, 수락률 약 79%)가 단계별(per-step) 속도 저하를 상쇄하지 못합니다.
27B 모델의 경우 MTP가 도움이 됩니다. 모델이 GPU에 완전히 들어맞기 때문입니다 (12.45 GB). --fit-target 0은 MTP 사용 여부와 상관없이 작동하므로 VRAM 페널티가 없습니다. 27B 모델은 MTP 미사용 시(이전 빌드 기준) 약 56 tok/s에서 MTP 사용 시 73 tok/s로 향상되었습니다.
경험 법칙(Rule of thumb): MTP는 모델이 GPU에 완전히 들어갈 때는 도움이 되지만, MTP 연산 버퍼(compute buffer)로 인해 더 많은 레이어가 CPU로 넘어가게 되면 오히려 성능을 저하시킵니다.
코딩 에이전트 컨텍스트 길이에서의 속도 (실제 테스트)
모두가 코딩 에이전트(coding agents)를 128k 컨텍스트에서 실행합니다. 컨텍스트 윈도우(context window)를 채워감에 따라 실제로 어떤 일이 발생하는지 확인해 보겠습니다. 합성 프롬프트(Python 클래스, 아키텍처 문서, 에러 스택 트레이스 — 토크나이저 압축을 방지할 만큼 충분히 다양함)를 사용하였고, 프롬프트 캐시(prompt cache)는 비활성화했으며, --fit-target 1536 옵션을 적용한 35B Q4_K_XL 모델로 테스트했습니다:
| 컨텍스트 | PP (MTP 미사용) | PP (MTP 사용) | TG (MTP 미사용) | TG (MTP 사용) |
|---|---|---|---|---|
| ~8k | 1,855 tok/s | 1,712 tok/s | 73 tok/s | 79 tok/s |
| ... | ||||
| 8k/32k TG는 64k/128k와 별도의 실행에서 측정되었습니다 — 측정 노이즈로 인해 행 간에 약 5-10%의 편차가 발생할 수 있습니다. |
128k 컨텍스트에서는 MTP 사용 시와 미사용 시의 TG 속도가 동일하게 수렴합니다 (~56 tok/s). 긴 컨텍스트에서는 MTP 여부와 상관없이 KV 캐시(KV cache)가 VRAM을 채우기 때문에, 오프로드(offload) 분할 결과가 동일해집니다. MTP의 멀티 토큰 추측(multi-token speculation) 효과가 연산 오버헤드(compute overhead)에 의해 상쇄되는 것입니다.
PP는 점진적으로 저하됩니다: 8k에서 128k로 갈 때 1,855 → 1,584 tok/s로 감소합니다 (~15% 하락). 128k 프롬프트는 약 81초 내에 처리됩니다.
"97 tok/s"는 --fit-target 0을 사용한 짧은 컨텍스트에서만 존재합니다. 64k 이상에서는 KV 캐시가 성장할 여유 공간(headroom)이 없기 때문에 --fit-target 0 설정 시 OOM(Out of Memory)이 발생합니다. 긴 컨텍스트 작업을 위해서는 반드시 --fit-target 1536을 사용해야 하며, 이 경우 짧은 컨텍스트에서는 속도가 ~73 tok/s로, 128k에서는 ~56 tok/s로 떨어집니다.
코딩 에이전트를 위한 결론: 16GB 환경의 128k 컨텍스트에서 TG는 ~56 tok/s, PP는 ~1,500 tok/s를 기대하십시오. MTP는 큰 차이가 없습니다 — 전체 컨텍스트에서는 도움이 되지도, 해가 되지도 않습니다.
VRAM 사용량
| 설정 | 사용된 VRAM | 남은 VRAM | 비고 |
|---|---|---|---|
| A (27B IQ3+MTP) | 14,803 MiB | 1,039 MiB | 완전한 GPU 상주, fit-target 0 |
| ... |
컨텍스트 제한 (OOM 유도)
| 제한 | 27B IQ3 | 35B Q4_K_XL | 35B Q8_0 |
|---|---|---|---|
| 최대 컨텍스트 (q8_0 KV) | 56k | 131k+ | 131k+ |
| ... | |||
| 이것이 가장 큰 차별점입니다. 35B MoE는 하이브리드 아키텍처 (Gated DeltaNet + Attention) 덕분에 KV 캐시 (KV cache)가 필요한 풀 어텐션 (full-attention) 레이어가 약 10개뿐이므로 131k 컨텍스트를 쉽게 처리합니다. 나머지 SSM 레이어는 아주 작은 순환 상태 (recurrent state)를 사용합니다. 반면 27B 밀집 (dense) 모델은 모든 레이어에 KV가 존재하므로, q8_0 KV 사용 시 56k에서 최대치에 도달합니다. |
27B 사용자들을 위한 팁: -ctk q8_0 -ctv q8_0에서 -ctk q4_0 -ctv q4_0로 전환하면 최대 컨텍스트를 56k에서 110k로 확장할 수 있습니다. 품질 저하는 미미합니다. 56k 컨텍스트에서의 q4_0 KV는 CodeNeedle 점수 218/220을 기록했으며, 이는 q8_0 KV의 220/220과 비교됩니다 (일반적인 컨텍스트에서의 q4_0는 219/220이므로, 2점 차이의 대부분은 긴 컨텍스트 때문이 아니라 q4_0 자체의 특성 때문입니다).
높은 컨텍스트에서 발생하는 OOM (Out of Memory)은 KV 캐시 자체가 아니라 MTP 연산 버퍼 (MTP compute buffer) (529 MiB 고정 할당) 때문입니다. 이는 llama.cpp의 구현 세부 사항이며 향후 버전에서 개선될 수 있습니다.
품질 — CodeNeedle (위치 기반 회상)
Python의 http.server에서 가져온 11개 함수, 약 50k 자의 코퍼스 (corpus)를 사용하여 정확한 라인 단위 회상 (recall)을 테스트했습니다:
| 지표 | 27B IQ3 | 35B Q4_K_XL | 35B Q8_0 |
|---|---|---|---|
| 통과 | 11/11 | 11/11 | 11/11 |
| ... | |||
| 27B IQ3는 만점을 기록했습니다. 모든 라인이 정확하며 환각 (hallucination)이 전혀 없습니다. 35B 모델들도 근접했으나 완벽하지는 않습니다. 여기서 Q8_0가 Q4_K_XL을 능가하지 못한다는 점이 흥미롭습니다. |
품질 — GSM8K (초등 수학, 100개 사례)
| 지표 | 27B IQ3 | 35B Q4_K_XL | 35B Q8_0 |
|---|---|---|---|
| 정확도 | 89% | 91% | 90% |
| ... | |||
| 세 모델 모두 신뢰 구간이 겹치며, 품질 차이는 무시할 수 있는 수준입니다. 하지만 35B Q4_K_XL은 평가 속도가 37% 더 빠르며 (67분 vs 106분), 절단 (truncation) 발생도 더 적습니다. |
*참고: 27B 모델로 AIME2025도 테스트했습니다. 전체적으로는 50%였으나, **절단되지 않은 사례에서는 100%*를 기록했습니다. 모든 실패 사례는 추론 오류가 아니라 32k에서의 컨텍스트 고갈 때문이었습니다. 131k 컨텍스트를 가진 35B MoE라면 아마 더 높은 점수를 받았을 것입니다.
Ubatch PP Trick (coder543, 5월 18일)
u/coder543는 --n-cpu-moe를 통해 부분적으로 오프로드(offload)된 모델의 경우, -ub 값을 512에서 8192로 늘리면 프롬프트 처리 속도(prompt processing speedup)가 5.5배 향상된다는 사실을 발견했습니다. 저는 이를 35B 모델로 테스트했습니다.
결과: --fit on 사용 시에는 적용되지 않음. -ub 2048+ 설정 시 OOM(Out of Memory)이 발생하는데, 이는 --fit on이 이미 모델 레이어를 위해 VRAM을 최대한 활용하고 있어 더 큰 배치 버퍼(batch buffers)를 위한 여유 공간(headroom)이 없기 때문입니다. 대신 --n-cpu-moe 수동 오프로드를 사용하면 이 트릭이 작동합니다. 하지만 --fit on이 더 간편하며 레이어 분할을 자동으로 처리합니다.
병렬 처리 (-np sweep)
10개의 GSM8K 케이스에 대해 -np 1/2/4를 테스트했습니다:
| -np | 27B tok/s | 27B throughput | 35B tok/s | 35B throughput |
|---|---|---|---|---|
| 1 | 83.3 | 0.6 cases/min | 70.7 | 0.8 cases/min |
| ... |
-np 2는 요청당 속도가 30% 느려지는 대신 배치 처리량(batch throughput)을 두 배로 늘려줍니다. -np 4는 레이어를 CPU로 밀어냅니다. 이 경우 27B는 10 tok/s로 떨어지고, 35B는 부분적으로 실패합니다. 대화형 채팅에는 -np 1을, 배치 평가(batch evaluation)에는 -np 2를 사용하세요.
MTP 참고 사항 (27B / GPU 전체 탑재 설정용)
MTP는 모델이 GPU에 완전히 들어갈 때(오프로드 페널티가 없을 때) 가치가 있습니다. 12GB 환경의 27B IQ3 모델의 경우, MTP 사용 시 73 tok/s, 미사용 시 약 56 tok/s를 기록했습니다. 16GB 환경의 35B 모델의 경우, MTP를 건너뛰세요(위의 속도 표 참조).
만약 MTP를 사용한다면:
--spec-type draft-mtp를 사용하세요 —mtp가 아닙니다. 메인라인(Mainline)에서 이름을 변경했습니다.-np 1을 사용하세요 — b9204 버전은 기본값이 4 슬롯(slots)으로 설정되어 있어 레이어를 CPU로 밀어냅니다.--spec-draft-n-max 2가 3보다 더 낫습니다 (3일 때 수락률(acceptance)이 낮아져 전체적으로 더 느려집니다).- 부분 오프로드 모델의 경우
--fit-target 1536을 사용하세요. GPU에 완전히 탑재하는 경우--fit-target 0을 사용합니다. - 128k 컨텍스트에서는 MTP가 속도 향상을 주지 못합니다 — 컨텍스트 길이에 상관없이 KV 캐시(KV cache)가 VRAM을 점유하기 때문입니다.
기타 참고 사항:
- **Hadamard KV rotation (
-khad)**은 b8607부터 기본적으로 활성화되어 있으므로 별도의 플래그가 필요하지 않습니다. -np 2는 요청당 속도가 30% 느려지는 대신 배치 처리량을 두 배로 늘립니다. 평가에는 좋지만, 대화형에는 좋지 않습니다.
권장 사항
설정 (그대로 복사해서 사용하세요)
./llama-server \
-m Qwen3.6-35B-A3B-MTP-UD-Q4_K_XL.gguf \
-c 131072 -np 1 --fit on --fit-target 1536 \
-fa on -t 20 --no-mmap --jinja \
-ctk q8_0 -ctv q8_0
MTP 없음. 특별한 플래그 없음. --fit-target 1536이 핵심입니다. 이 설정은 VRAM 여유 공간을 확보하여 128k 컨텍스트에서 KV 캐시로 인한 OOM(Out of Memory)이 발생하지 않도록 합니다. 모델을 로드하고, 실행 상태로 둔 채, 귀하의 코딩 에이전트(coding agent)를 localhost:8080/v1/chat/completions로 지정하세요.
결과물: 128k 컨텍스트에서 56 tok/s 생성 속도. 1,584 tok/s 프롬프트 처리 속도 (128k 토큰을 처리하는 데 81초 소요). 최대 131k 컨텍스트. GSM8K 91%. 안정적임.
왜 MTP를 사용하지 않나요? 128k 컨텍스트에서는 MTP를 사용하는 경우와 사용하지 않는 경우 모두 동일한 56 tok/s를 보여줍니다. 어떤 방식이든 KV 캐시가 VRAM을 지배하기 때문입니다. MTP는 이득 없이 5가지의 주의사항(gotchas)만 추가할 뿐입니다. 복잡함을 건너뛰세요.
GGUF: havenoammo/Qwen3.6-35B-A3B-MTP-GGUF (MTP GGUF는 --spec-type draft-mtp 없이도 잘 작동하며, 단지 추가 텐서들을 무시할 뿐입니다).
27B GGUF: GazTrab/Qwen3.6-27B-MTP-UD-IQ3_XXS-GGUF
기타 VRAM 예산 (커뮤니티 데이터, 당사 테스트 결과 아님)
위의 모든 사항은 당사의 RTX 5080 16GB에서 테스트되었습니다. 다른 GPU에 대한 이 추정치들은 커뮤니티 보고서에서 가져온 것입니다:
| VRAM | 모델 | 속도 | 출처 |
|---|---|---|---|
| 8 GB | 27B IQ3 (MTP 미사용) | ~50 tok/s (추정) | 모델 크기가 12.45 GB이므로 부분적인 오프로드(offload)가 필요함 |
| ... | |||
27B IQ3+MTP는 MTP 헤드를 이식(grafted)해야 합니다 — 리포지토리의 graft-mtp.py를 사용하세요. |
왜 다른 모델들은 안 되나요?
27B IQ3 — 당사의 16GB 카드에서 테스트했을 때 GPU에 완전히 들어갑니다 (12.45 GB 모델). 완벽한 CodeNeedle (220/220), MTP 사용 시 73 tok/s (GGUF). 하지만 컨텍스트가 56k에서 제한됩니다 (q4_0 KV 사용 시 110k). 만약 귀하의 코딩 에이전트가 128k를 필요로 한다면, 이 모델은 제외됩니다. 35B 모델이 들어가지 않는 12 GB 카드에 더 적합합니다.
35B Q8_0 — 38% 더 느리며 (MTP 사용 시 46 tok/s), 품질 향상은 미미합니다 (GSM8K 90% vs 91%, CI가 겹침). 16 GB에서 이 모델을 위해 VRAM을 할애할 가치가 없습니다.
크레딧
이 포스트는 커뮤니티 덕분에 존재합니다:
- am17an — 원본 MTP 구현 (PR #22673), 메인라인 b9190에 병합
- havenoammo — MTP GGUF 변형 모델 + graft 스크립트
- u/janvitos — 12GB에서 80 tok/s MTP 설정 (635개 추천), 플래그(flags) 문서화
- u/coder543 —
--n-cpu-moe를 위한 ubatch PP 트릭 (5월 18일) - u/OsmanthusBloom — 초기 ubatch 발견
- u/Still-Notice8155 — 어디서나 작동함을 증명한 GTX 1070 8GB MTP 벤치마크
- u/raketenkater — run-time-repack, defrag-thold, -khad 플래그 문서화
- u/moflinCASIO — 4060 Ti 16GB 레퍼런스 벤치마크
- u/WarthogConfident4039 — 이번 벤치마킹 라운드 요청
- ggerganov — llama-eval, MTP 메인라인 병합
- u/simracerman — PP 속도 벤치마크 추진 ("일반적인 코딩 에이전트는 10k 토큰을 쏟아냅니다")
- u/danielhanchen (Unsloth) — UD-Q4_K_XL 이면에 있는 동적 양자화 (Dynamic quantization) 공식
- u/alexziskind1 — CodeNeedle 위치 재현 (positional recall) 벤치마크
향후 계획
vLLM vs llama.cpp 정면 승부. vLLM >= 0.19.0은 PagedAttention (동적 KV 할당 — VRAM을 점유하는 고정 컴퓨팅 버퍼 없음)을 통해 MTP를 네이티브로 지원합니다. 이는 부분 오프로드 (partial-offload) 모델에서 MTP를 실제로 더 빠르게 만들 수도 있습니다. 계속 지켜봐 주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Reddit AI Engineering의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기