llama.cpp: UD-IQ4_XS GGUF; MTP는 qwen4exp/mtp 포크에서 테스트
SGLang 및 FreeToken: 동일한 NVFP4 체크포인트 리비전 사용
클라이언트: AIPerf, thinking 기능 끄고 동일한 non-thinking 샘플러 사용
엔진 하나씩 돌릴 때마다 매번 새로 시작하고 GPU 쿨다운 시키면서 진행했어. 실행 결과는 설정값, 출력물, 메모리 사용량, GPU 텔레메트리까지 다 저장해둠.
비교 시 주의사항
이 결과는 이 워크스테이션에서 테스트한 스택 기준이야. 양자화, KV-캐시 포맷, 메모리 배치, 추론 가속 방식이 다 다르다는 걸 감안해줘.
SGLang은 자체 NEXTN 드래프트 헤드를 쓰고, FreeToken은 테스트 환경에서 추론 가속을 적용 못 했어(작동을 안 하더라). llama.cpp는 기본값이랑 MTP 결과를 따로 냈고.
그러니까 이 제목만 보고 엔진 소프트웨어 성능만 딱 잘라 말할 순 없어. 레포지토리에 설정값 다 올려놨으니까 직접 확인해봐.
테스트한 최신 빌드, PR 및 추론 가속
llama.cpp MTP:danielhanchen/llama.cpp의 qwen4exp/mtp 포크, d1a92352 커밋 고정, 약 2.6GB 드래프트 헤드 사용. 코딩 테스트에서 헤드 끄고 켤 때 비교해보니 8K에서 1.63배, 32K에서 1.69배 빨라짐.
SGLang on Blackwell: 테스트한 이미지에는 PR #36567, #36556, #36749, #36750이랑 로컬 FP8 KV-캐시 패치가 포함됨. 이건 개별 벤치마크가 아니라 전체 설정의 일부야.
N-gram 추론:ngram-mod는 테스트한 코드 작업에서 디코딩 속도를 6.8% 향상시켰지만, 24-토큰 매치 설정으로 일반 문장 테스트했을 땐 드래프트가 하나도 안 생겼어.
실험적인 PLE 읽기: llama.cpp PR #28136을 빌드해봤는데, 플래그 이름이 바뀌어서 무시됐다는 걸 나중에 알았어. 의도했던 직접 읽기 모드가 작동을 안 해서, 이 PR로 인한 속도 향상은 주장하지 않을게.
성공한 테스트뿐만 아니라 실패하거나 철회한 결과도 리포트에 다 기록해뒀어.
1. 네 가지 설정 모두 전체 윈도우를 다 수용함. 대기 시간은 천차만별.
이 테스트는 약 261,500개의 입력 토큰과 128개의 출력 토큰을 262,144 토큰 윈도우 안에서 돌린 거야. 설정마다 입력 토큰 수가 2개 정도 차이 나긴 해.
Configuration
First token
Decode
SGLang
35.4s
126.9 tok/s
FreeToken
80.4s
87.5 tok/s
llama.cpp + MTP
210.2s
52.6 tok/s
llama.cpp baseline
258.4s
20.3 tok/s
4분이 넘게 걸리던 게 35초로 줄어드니까 대형 프롬프트 쓸 때 체감이 완전히 다르네.
두 열은 서로 다른 걸 측정함. 첫 토큰 시간(first-token time)은 처음 대기하는 시간이고, 디코드(decode)는 그 이후에 답이 얼마나 빨리 나오는지임.
2. 짧은 프롬프트 테스트로는 긴 컨텍스트에서의 동작을 제대로 알 수 없음.
별도의 산문 테스트는 2,048 토큰 답변을 사용했고, 입력 길이에 따라 요청을 세 번씩 측정함.
Configuration
Decode at 2K input
Decode at 259,584 input
SGLang
182.7 tok/s
191.5 tok/s
FreeToken
100.1 tok/s
94.8 tok/s
llama.cpp + MTP
126.8 tok/s
61.4 tok/s
llama.cpp baseline
101.9 tok/s
20.2 tok/s
프리필(prefill)도 순위를 바꿔놓음. 2K 입력에서 FreeToken은 llama.cpp보다 뒤처짐: 1,525 vs 1,869 tok/s. 근데 128K에서는 3,231 vs 1,362 tok/s로 약 2.4배 더 빠름.
3. MTP가 llama.cpp에 도움은 되는데, 긴 프롬프트 대기 시간까지 없애주진 않음.
실제 코딩 프롬프트에서 드래프트 헤드(draft head)를 끄고 켰을 때의 빌드를 비교해 봄:
Input
MTP off
MTP on
Decode gain
8,192 tokens
94.9 tok/s
155.1 tok/s
1.63x
32,000 tokens
83.1 tok/s
140.4 tok/s
1.69x
드래프트 헤드는 대략 2.6GB 정도임.
전체 윈도우에서 테스트한 MTP 설정은 52.6 tok/s가 나왔고, 기본 설정은 20.3 tok/s였음. 2.59배 차이긴 한데, 전체 윈도우 비교는 빌드 자체가 달라서 생기는 변수도 있음. 위에 있는 동일 빌드 코딩 테스트가 드래프트 헤드 변화를 더 확실하게 보여줌.
258초에서 210초로 첫 토큰 시간이 줄어든 걸 단순히 MTP 덕분이라고만 보긴 어려움.
4. 속도뿐만 아니라 정확도도 확인해 봄.
Stack
GSM8K
MATH-500
llama.cpp baseline
95.60%
92.60%
SGLang
95.22%
93.00%
FreeToken
95.75%
92.20%
llama.cpp MTP는 **GSM8K에서 95.75%**를 찍음.
테스트에는 GSM8K 문제 1,319개랑 MATH-500 문제 500개를 썼음. 쌍으로 비교해 봐도 통계적으로 유의미한 차이는 안 보임.
그렇다고 스택들 성능이 똑같다는 건 아님. 이건 그냥 짧은 수학 벤치마크 두 개일 뿐이고, 이 기기에서 전체 정밀도(full-precision)를 비교할 기준점도 없으니까.
5. 모델 시작 시간도 따로 벤치마크해 봄.
컨테이너 시작부터 첫 답변을 받기까지 걸린 중간값:
llama.cpp: 16초
SGLang: 108초
FreeToken: 126초
FreeToken은 약 3.3초 만에 /health에서 HTTP 200을 뱉긴 했는데, 서비스 준비가 되는 데 82초 정도 걸렸고, 첫 생성까지 44초가 더 걸림.
첫 요청에는 컴파일 작업이 포함되어 있음. 헬스 체크 엔드포인트만 측정하면 시작 속도에 대해 완전히 잘못된 결론을 내릴 수 있음.
6. 전문가(expert)가 GPU에 있을 때는 로딩 모드에 따른 속도 차이가 거의 없음.
같은 llama.cpp 이미지에서 텐서 배치도 똑같이 하고 실제 코딩 프롬프트를 써서 none, mmap, mlock, mmap+mlock, dio를 비교해 봄.
8K 입력에서 프리필은 2,036 ~ 2,124 tok/s — 4.3% 차이.
32K에서는 1,946 ~ 1,956 tok/s — 약 0.5% 차이.
메모리 부족으로 뻗거나 재시작된 경우는 없었음.
예전에 내가 했던 1.87배 RAM 상주 로딩 이득 테스트는 배치가 달랐음. 그때는 전문가 레이어 23개를 CPU에서 돌렸거든. 이번 테스트에선 전문가 전부를 GPU에 박아둠.
CPU가 전문가를 계산할 때는 로딩 모드가 중요할 수 있는데, 이번 설정에선 별 차이 없었음.
7. 전문가를 CPU로 오프로드하니까 MTP가 오히려 느려짐.
위에서 본 MTP 이득이 모든 메모리 예산 상황에서 다 적용되는 건 아님.
같은 RTX PRO 6000에서 VRAM 가용 풀을 더 작게 잡고, 2,048 토큰짜리 코딩 프롬프트와 256 토큰짜리 답변으로 테스트를 다시 돌려봤음. 양쪽 다 동일한 포크 빌드를 사용함.
Usable VRAM
Expert layers on CPU
MTP off
MTP on, head on GPU
16 GiB
45
33.0 tok/s
9.5 tok/s
24 GiB
42
34.7 tok/s
10.2 tok/s
32 GiB
36
38.1 tok/s
11.9 tok/s
48 GiB
23
48.3 tok/s
18.2 tok/s
96 GiB
0
99.8 tok/s
160.2 tok/s
96 GiB 전체 용량을 다 쓸 때는 MTP가 디코딩 속도를 1.61배 더 빠르게 만들었음. 근데 24 GiB로 줄이니까 오히려 디코딩이 3.4배 정도 더 느려지더라.
드래프트 헤드를 CPU로 옮겨봐도 24 GiB 결과는 그대로였음. MTP를 껐을 때 34.7 tok/s가 나오던 게 MTP를 켜니까 9.6 tok/s로 박살 남.
이번 테스트를 보면, MTP는 모든 전문가 모델(expert)이 GPU에 상주할 때만 효과가 있음. 드래프트 토큰을 검증하는 과정에서 작업량이 늘어나는데, CPU에서 전문가 모델을 돌리는 비용이 얻는 이득보다 훨씬 크기 때문임.
이건 Blackwell 카드 한 장에서의 VRAM 용량 제한을 테스트한 거지, 실제 저사양 GPU에서 측정한 게 아님. 실제 GPU는 대역폭이나 연산 성능이 다르니까 참고하셈. 룩업 테이블은 양쪽 다 빌드 기본값인 lazy-read 모드를 사용했음.
8. 작업이 빨리 끝나면 요청당 GPU 에너지 소모도 줄어듦.
128 토큰 답변이 포함된 풀 윈도우 테스트에서, GPU 에너지는 작업 중 중앙값 전력 × 요청 처리 시간으로 계산했음.
Configuration
Median GPU power
Approximate GPU energy
SGLang
358 W
13 kJ
FreeToken
404 W
33 kJ
llama.cpp + MTP
489 W
104 kJ
llama.cpp baseline
440 W
116 kJ
가장 빠른 설정은 이번 요청에서 기준치 대비 GPU 에너지 소모가 대략 9분의 1 수준이었음.
가장 큰 차이는 GPU가 얼마나 오래 일하느냐였음. 생성 과정을 포함해서 SGLang은 약 36초, llama.cpp는 약 265초가 걸렸거든.
이건 중앙값 전력을 사용한 추정치일 뿐, 실제 통합 에너지나 벽면 콘센트 측정값이 아님. CPU, RAM, SSD 전력은 제외했음. 이번 테스트 중에 쓰로틀링은 없었음.
혹시 다른 GPU나 메모리 환경에서 이 엔진들로 같은 모델 테스트해 본 사람 있음?
나는 특히 FreeToken이 긴 컨텍스트에서도 이렇게 성능이 일정하게 유지되는지, 그리고 전문가 모델 일부를 CPU로 오프로드했을 때 MTP가 다른 모델들에서 얼마나 효과가 있는지 궁금함.
혹시 더 효율적으로 돌리는 방법 찾은 사람 있으면 공유 좀.
주요 댓글
r/localllama
사용자들은 Llama.cpp의 사용 편의성을 인정하면서도, 고성능 환경에서는 하이퍼 최적화된 커스텀 커널이 더 효율적이라는 점에 공감하고 있습니다.
내 40GB VRAM에 UD-Q6_K로 풀 unquantized KV 캐시까지 다 들어가는데, 퀄리티는 FP8이랑 비슷함. 근데 vLLM으로 FP8 돌리면 컨텍스트 최대 90k 정도밖에 안 나올 듯. 흥미로운 건 FP8/INT8 양자화가 디코드할 때 llama.cpp의 Q6보다 오히려 조금 더 느리다는 거임.
3
Llama.cpp는 속도 대신 사용 편의성과 호환성을 택한 거지. GPU 사양이 안 맞거나 VRAM으로 오프로드해야 하는 많은 사용자에게 Llama.cpp는 최고의 엔진이야. 하지만 동일한 GPU로 구성된 환경이고 VRAM 오프로드가 필요 없다면 Llama.cpp를 쓸 이유는 거의 없지(모델 전환이 빠르다는 점 정도?).
1
결국 끝판왕은 Ninfer나 halogen-flash-server처럼 하이퍼 최적화된 커스텀 커널이 될 거야. Claude, OpenAI, Gemini 같은 클라우드 AI 기업들은 100% 커스텀 하이퍼 최적화 커널을 쓰고 있을 거라고 확신해. 사용 편의성과 폭넓은 지원을 위해 만들어진 Llama.cpp보다 훨씬 효율적이니까.
이 AI 시대에는 GPU 1개, 모델 1개만 지원하는 일회성 프로그램을 짜는 게 충분히 용인돼. 예전 같았으면 미쳤다는 소리를 들었겠지만, 지금은 대기업들도 그렇게 하니까...