fast (Gemma 4 26B-A4B):
- Enable mmproj GPU loading (vision ~1s, 12x faster than CPU)
- KV f16 → q8_0 (save ~2.5 GB VRAM for mmproj)
- Tensor split 0.5,0.5 → 0.43,0.57 (13/17 layers)
- Remove --mlock/--poll/--prio/-t/-tb (no measurable impact)
- measured_tps 74.65 → 71.89 (trade 3.7% speed for vision)
balanced (Qwen 3.5 35B-A3B):
- Tensor split 0.5,0.5 → 0.48,0.52 (enables pipeline parallelism)
- Ubatch 128 → 256 (prefill +78%: 649 → 1,157 t/s)
- mmproj + --no-mmproj-offload (CPU vision, VRAM headroom)
- Remove useless flags same as fast
- measured_tps 61.62 → 64.16 (+4.1%)
Other:
- Document full retuning in docs/v3_{fast,balanced}_retuning_log.md
- Session report at .planning/reports/20260411-session-report.md
- Add bench utilities: bench_short/bench_long/test_ts_ratios
- Speculative decoding (E2B draft) experimented but rejected
(+14% gen vs -31% cold start + tokenizer mismatch + mmproj conflict)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8.8 KiB
v3 — Balanced Role Retuning Log (Qwen 3.5 35B-A3B)
Date: 2026-04-11
Target role: balanced (Qwen3.5-35B-A3B Q4_K_M)
Goal: 기존 measured_tps: 61.62 기준을 재검증하고, 진짜 최적 구성을 실측 기반으로 확정
Result: 최종 TPS 64.16 t/s (짧은 프롬프트) / 62.00 t/s (3,100 토큰 긴 프롬프트)
Status: ✅ 확정
1. 재튜닝 동기
Phase 01 종료 후 engine_models.json의 balanced 설정이 여러 이유로 일관되지 않게 수정되어 있었음:
--mmproj추가 (비전 지원용, 다른 작업자가 넣음)--mlock --poll 50 --prio 3등 Phase 01 최종본과 불일치-ts 0.5,0.5(이중 GPU 분할) 상태에서 compute buffer OOM 발생- 실측 속도가 레퍼런스(61.62) 대비 33~42 t/s 수준으로 떨어짐
재튜닝을 통해 원인 규명 + 안정 + 최적 설정 확정이 목표.
2. 하드웨어 진단 (핵심 발견)
| GPU | 모델 | 최대 PCIe | 현재 |
|---|---|---|---|
| 0 | RTX 3060 12GB | Gen3 x16 | Gen3 x4 (슬롯 4레인) |
| 1 | RTX 3060 12GB | Gen4 x16 | Gen4 x16 |
핵심: GPU 0은 PCIe 3.0 × 4 슬롯에 있음 (3.94 GB/s). GPU 1은 PCIe 4.0 × 16 (31.5 GB/s). GPU 0이 GPU 1의 1/8 대역폭.
이 비대칭이 모든 하이브리드/멀티-GPU 성능 문제의 근본 원인.
3. Qwen3.5-35B-A3B 아키텍처 실측 데이터
llama-server 로드 로그 기준:
architecture = qwen35moe
file size = 20.49 GiB (Q4_K_M, 5.08 BPW)
n_params = 34.66 B
n_layer = 40
n_head_kv = 2
n_embd_head_k = 256
n_embd_head_v = 256
n_expert = 256 (activated: 8)
full_attention_interval = 4
중요: 40 레이어 중 full attention은 10개만 (매 4번째). 나머지 30개는 Gated Delta Net (SSM/Mamba-like) 레이어로 recurrent state 사용. KV 캐시는 10 레이어에만 발생.
KV 캐시 실측
| 컨텍스트 | KV 캐시 (q4_0) |
|---|---|
| 128K | 720 MiB |
| 256K | 1,440 MiB |
(초기 추정 5GB는 오류였음 — 40레이어 전부 attention이라고 오해)
4. -ts (Tensor Split) 비율 스윕 결과
자동화 스크립트(scripts/test_ts_ratios.py)로 여러 비율 테스트:
| ratio | status | PP | c0 model | c1 model | c0 compute | c1 compute |
|---|---|---|---|---|---|---|
| 0.5,0.5 | ready | Fallback | 10,540 | 9,931 | 203 | 123 |
| 0.48,0.52 | ready | ✅ ON | 10,036 | 10,434 | 600 | 384 |
| 0.47,0.53 | ready | ✅ ON | 10,036 | 10,434 | 600 | 384 |
| 0.45,0.55 | error | — | — | — | — | — |
| 0.43,0.57 | error | — | — | — | — | — |
| 0.40,0.60 | error | — | — | — | — | — |
해석:
- 40 레이어 기준 llama.cpp가 ratio를 정수 레이어로 반올림: 0.48 & 0.47 둘 다 19/21 분할이라 동일한 메모리 배치
- 0.5,0.5 (20/20)에서는 CUDA0 compute buffer가 PP 모드 요구치(600 MiB)를 수용 못해 자동 Fallback
- 0.45,0.55 이상은 CUDA1이 22+ 레이어 적재로 OOM
- 결론: PP on 유일 비율은 0.48,0.52 (또는 동등한 0.47,0.53)
5. -ub (Ubatch) 스윕 결과 — 핵심 발견
짧은 프롬프트만 테스트해서 처음에 -ub 효과를 놓쳤음. 긴 프롬프트(3,100 토큰)로 재측정:
| 설정 | PP | Prompt t/s | Gen t/s | 3,100 토큰 prefill | GPU0 여유 |
|---|---|---|---|---|---|
| ub 128 | ✅ ON | 649 | 62.01 | 4.85s | 216 MiB |
| ub 256 | ❌ OFF | 1,157 | 62.00 | 2.68s | 411 MiB |
| ub 384 b 768 | ❌ OFF | 1,275 | 61.61 | 2.43s | 133 MiB |
핵심 인사이트:
-
-np 1단일 사용자 환경에서 PP는 실질 이득 없음 — Pipeline Parallelism은 다중 요청 배칭에 의미가 있음. 단일 시퀀스면 overlap 할 대상이 없음. -
PP off가 오히려 유리 — compute buffer 작아져서
-ub더 올릴 수 있음 → prefill 속도 대폭 향상 -
-ub수익률 체감:- 128 → 256: +78% (649 → 1,157 t/s)
- 256 → 384: +10.2% (1,157 → 1,275 t/s)
- 안정성 대비 256이 스윗 스팟
-
Gen 속도는
-ub와 무관 — 모두 62 t/s. Gen은 KV 캐시 크기 + PCIe x4 병목이 결정.
6. mmproj 처리 결정
Qwen3.5-35B-A3B는 natively 멀티모달이라 mmproj 필요. 하지만 GPU에 올리면:
VRAM 수지 (256K, -ts 0.48,0.52):
모델 가중치: 10,036 (GPU0) + 10,434 (GPU1)
KV 캐시: 720 + 720 = 1,440 MiB
Compute: ~600 + ~384 MiB
mmproj: 858 MiB ← 추가 부담 → OOM
해법: --no-mmproj-offload 추가 → mmproj를 CPU RAM에 유지.
| 항목 | GPU 오프로드 | CPU 오프로드 |
|---|---|---|
| VRAM 절약 | — | 858 MiB |
| 텍스트 추론 | 동일 | 동일 (손실 0) |
| 이미지 인코딩 | GPU 빠름 | CPU 6.4초 (640×640 기준) |
Hermes Agent 사용 패턴 = 95% 텍스트, 가끔 스크린샷 → CPU 오프로드가 확실히 유리.
이미지 토큰 계산식
patch_size = 16
n_merge = 2
→ tokens = (width/32) × (height/32)
| 해상도 | 토큰 |
|---|---|
| 640×640 | 400 |
| 768×768 | 576 |
| 1024×1024 | 1,024 (권장 최소) |
| 2048×2048 | 4,096 (최대) |
7. 제거된 옵션 (실측 영향 없음 확인)
| 옵션 | 제거 이유 | Δ TPS |
|---|---|---|
--mlock |
전용 추론기. 시스템 RAM 여유. mmap 페이지 잠금 불필요 | 0.04 |
--poll 50 |
GPU polling. -np 1 환경에선 효과 없음 |
0.00 |
--prio 3 |
프로세스 우선순위. 전용기라 경쟁 없음 | 0.00 |
제거 후 속도: 64.16 t/s (유지)
8. 최종 확정 옵션
{
"balanced": {
"display_name": "Qwen 3.5 35B (Balanced)",
"model_path": "models/Qwen3.5-35B-A3B-Q4_K_M.gguf",
"measured_tps": 64.16,
"args": [
"--mmproj", "models/mmproj-F16.gguf",
"--no-mmproj-offload",
"-ngl", "999",
"-c", "262144",
"-np", "1",
"-fa", "on",
"--cache-type-k", "q4_0",
"--cache-type-v", "q4_0",
"-ub", "256",
"-b", "512",
"-t", "6",
"-tb", "6",
"-ts", "0.48,0.52"
]
}
}
9. 최종 실측 성능
텍스트 추론
| 시나리오 | Prompt t/s | Gen t/s | VRAM 여유 |
|---|---|---|---|
| 짧은 프롬프트 (170 tok) | — | 64.16 | GPU0 411 / GPU1 539 MiB |
| 긴 프롬프트 (3,100 tok) | 1,157 | 62.00 | 동일 |
비전 추론 (mmproj CPU)
| 단계 | 속도 / 시간 |
|---|---|
| 이미지 인코딩 (CPU, 640×640) | 5.3초 (encode) + 1.0초 (decode) = 6.4초 |
| 이미지 이후 생성 | 62 t/s |
VRAM 최종
GPU 0 11,900 MiB (여유 216 MiB) Gen3 x4 [PCIe 병목]
GPU 1 11,710 MiB (여유 401 MiB) Gen4 x16
합계 23,610 MiB 중 사용 │ 966 MiB 여유
CPU RAM
llama-server Working Set: ~23 GB
├── mmap 모델 (lazy) 20.5 GB
├── mmproj (CPU 할당) 0.86 GB
├── CUDA_Host compute buffer 0.39 GB
├── CPU compute buffer 0.25 GB
└── 기타 ~0.08 GB
10. 알려진 구조적 제약
- GPU 0 PCIe 3.0 x4 슬롯 병목 — Gen 속도 62 t/s 상한의 주원인. 물리적 한계라 소프트웨어로 해결 불가.
- Pipeline Parallelism 자동 Fallback — compute buffer가
-ub 256시 CUDA0 한계 초과. 다만-np 1환경에선 실질 손실 없음. - mmproj CPU 상주 — 이미지 인코딩 시 GPU 대비 ~3-5배 느림. 사용 빈도가 낮아 허용.
향후 개선 여지
- GPU 0을 PCIe 4.0 x16 슬롯으로 물리 이전 시 Gen 속도 추가 이득 기대 (~70+ t/s 가능성)
- 비전 사용이 잦아지면
--no-mmproj-offload재고 필요
11. 레퍼런스 대비
Phase 01 측정치: 61.62 t/s
v3 확정치: 64.16 t/s (+4.1%)
Phase 01은 단일 GPU 환경에서 튜닝되었음 (found 1 CUDA devices 로그 확인). 현재는 듀얼 GPU (비대칭 PCIe) + mmproj 제약 + PP 동작을 모두 반영한 새로운 baseline.
12. 검증 절차 (재현용)
# 기동
run_variet_engine.bat
# 짧은 프롬프트 속도
curl -s http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"balanced","messages":[{"role":"user","content":"Write 200 words about computing history."}],"max_tokens":300}' \
| python -c "import json,sys; d=json.load(sys.stdin); print(d['timings']['predicted_per_second'])"
# 긴 프롬프트 속도
python scripts/bench_long.py "verify"
# VRAM 확인
nvidia-smi --query-gpu=index,memory.used,memory.free,pcie.link.gen.current,pcie.link.width.current --format=csv
Document sealed: 2026-04-11