在 6GB 显卡上跑 22GB AI 模型 - llama.cpp MoE 调优指南¶
22GB 的模型塞进 6GB 显卡,还能 17 tokens/s 流畅生成。核心不是魔法,是 MoE(Mixture of Experts)架构的稀疏性 + llama.cpp 的细粒度权重分工。这篇笔记拆解从 3 t/s 到 17 t/s 的 5 个关键 flag,以及 3 个调优会撞墙的瓶颈。
目录¶
- MoE 架构原理:为什么 22GB 模型能塞进 6GB
- 5 个关键调优参数(3→17 t/s)
- Prompt 处理与 KV Cache 优化
- 技术陷阱与极限瓶颈
- 4-bit 量化质量评估
- 总结与落地建议
一、MoE 架构原理:为什么 22GB 模型能塞进 6GB¶
稀疏激活:35B 参数,每个 token 只用 3B¶
Qwen3.6-35B-A3B(阿里巴巴,2026-04-15 发布,Apache 2.0)总参数 350 亿,但生成每个 token 时仅有 30 亿参数(3B) 被实际激活。
模型内部有 256 个路由专家(Routed Experts),每个 token 由 Router 唤醒 8 个专家 + 1 个共享专家,其余 248 个保持静止。
这意味着 MoE 把问题从「模型塞不塞得进显存」变成了「哪些权重需要快速访问」。
┌─────────────────────────────────────────────────────────┐
│ Dense vs MoE 记忆体流量对比 │
├─────────────────────────────────────────────────────────┤
│ │
│ Dense 35B(密集型) │
│ 每 token 读取全部 35B 权重 → ~20 GB 记忆体流量 │
│ ████████████████████████████████ 20 GB/token │
│ │
│ MoE 35B-A3B(稀疏型) │
│ 每 token 仅读取 3B 激活权重 → <1 GB 记忆体流量 │
│ █ <1 GB/token │
│ │
│ 同等知识量,记忆体移动量降至 1/20 │
└─────────────────────────────────────────────────────────┘
记忆体层级分工¶
关键洞察:不是所有权重都需要同等速度。
| 权重类型 | 每 token 是否触发 | 应放在哪里 | 原因 |
|---|---|---|---|
| Attention(注意力) | ✅ 每个 token | GPU VRAM | 高频访问,需高带宽 |
| Embedding(嵌入层) | ✅ 每个 token | GPU VRAM | 高频访问 |
| Shared Expert(共享专家) | ✅ 每个 token | GPU VRAM | 恒定激活 |
| KV Cache | ✅ 每个 token | GPU VRAM | 随上下文增长 |
| Routed Experts(路由专家) | ❌ 大多数闲置 | 系统 RAM | 248/256 闲置,可慢速访问 |
核心思路:把高频权重放 GPU,低频权重放 RAM,用 PCI Express 按需搬运。
在 2025 年 7 月前,要实现专家权重卸载只能用手写正则表达式:
-ot "\.ffn_.*_exps=CPU",从论坛帖子复制并祈祷粘贴正确。现在有了专用 flag。
二、5 个关键调优参数(3→17 t/s)¶
基准平台:GTX 1060 6GB + 32GB DDR4 RAM。
| Flag | 作用 | 速度影响 | 难度 |
|---|---|---|---|
--n-cpu-moe N |
N 层专家权重留在 CPU | 3→10 t/s | ⭐⭐ |
--load-mode |
mmap/mlock 模式选择 | 10→13.5 t/s | ⭐⭐⭐ |
-ngl N |
图层回推 GPU | 13.5→17 t/s | ⭐⭐ |
--mlock |
锁定记忆体页面 | 稳定性提升 | ⭐ |
| microbatch 调优 | Prompt 处理加速 | 22→345 t/s | ⭐⭐⭐ |
Flag 1:--n-cpu-moe(核心参数)¶
# 将所有专家层放 CPU(最保守起点)
llama-server -m model.gguf --cpu-moe
# 将前 N 层专家放 CPU,逐步调降
llama-server -m model.gguf --n-cpu-moe 30
原理:将指定层数的 MoE 专家权重强制保留在系统 RAM 中,腾出 VRAM 给高频权重。
合并历史:
- 2025-07-31:--cpu-moe 合并进主线(PR 仅审核 80 分钟)
- 2025-08-04:--n-cpu-moe(可指定层数版本)跟进
调优方法(渐进测试法):
┌──────────────────────────────────────────┐
│ N 值调优流程 │
├──────────────────────────────────────────┤
│ │
│ --n-cpu-moe ALL ← 起点最保守 │
│ ↓ │
│ --n-cpu-moe 28 ← 逐步降低 │
│ ↓ │
│ --n-cpu-moe 26 │
│ ↓ │
│ --n-cpu-moe 24 │
│ ↓ │
│ ❌ VRAM 不足! │
│ ↑ 退回一步 │
│ ✅ --n-cpu-moe 24 = 正确值 │
│ (最后一个能装下的值) │
└──────────────────────────────────────────┘
工具限制:
| 工具 | 是否支持 --n-cpu-moe |
状态 |
|---|---|---|
| llama.cpp CLI | ✅ 完整支持 | 2025-08 起可用 |
| Ollama | ❌ 不支持 | Feature request 自 2025-08 仍 open;自动分配(实测 17% CPU / 83% GPU,无法干预) |
| LM Studio | ⚠️ 仅支持 all/none | 有 checkbox 但无法指定具体层数,ticket 仍 open |
视频明确建议:使用 llama.cpp 原生 CLI,暂勿用 Ollama / LM Studio 的 GUI。
Flag 2:--load-mode(记忆体映射模式)¶
llama.cpp 在 2025 年 3 月将旧参数(--no-mmap、--mmap、--mlock)整合为统一的 --load-mode:
| 模式 | 行为 | 适用场景 |
|---|---|---|
none |
不使用 mmap,全量载入 RAM | 模型 < 系统 RAM 时 |
mmap |
记忆体映射,由 kernel 决定驻留 | 默认值;模型 > 系统 RAM 时更优 |
mlock |
锁定页面,防止 swap | 长时间运行的服务 |
deo |
— | 特定优化路径 |
关键陷阱:如果模型大于系统 RAM(MoE 模型常达系统记忆体的 100%-150%),保留 mmap 反而更快——因为 OS Page Cache 在做实际工作。强制全量载入会导致频繁 swap,性能下降。
很多在线教程仍在教
--no-mmap,但该参数已打印 deprecation warning,现在统一用--load-mode。
Flag 3:-ngl N(GPU 图层回推)¶
# 专家移到 CPU 后,VRAM 空出来了,把 Transformer 常规图层推回 GPU
llama-server -m model.gguf --n-cpu-moe 24 -ngl 99
原理:专家权重移出后,6GB VRAM 大部分闲置。此时用 -ngl(--n-gpu-layers)将常态 Transformer 图层回推 GPU 处理,进一步加速解码。
调到 VRAM 填满为止。本案例中这一步让速度从 13.5 → 17 t/s。
Flag 4:--mlock(锁定记忆体页面)¶
仅在长期运行 server 时重要。Linux kernel 会在闲置数小时后将专家权重 page out 到 swap,导致「午餐后第一个 token 卡 4 秒」。--mlock 阻止此行为。
完整启动命令示例¶
llama-server \
-m qwen3.6-35b-a3b-Q4_K_M.gguf \
--n-cpu-moe 24 \
--load-mode mlock \
-ngl 99 \
-c 8192 \
-fa \
-tb 2048 \
-t 8
-tb= microbatch size(见下一节),-fa= flash attention,-t 8= CPU 线程数。
三、Prompt 处理与 KV Cache 优化¶
Prompt Processing(Prefill)是另一套机制¶
生成和读取 prompt 是两个完全不同的工作,llama.cpp 内部用不同路径处理。
CUDA 后端有一个常量 op_offload_min_batch_size(默认 32),它决定矩阵乘法由 CPU 还是 GPU 执行:
┌─────────────────────────────────────────────────────────────┐
│ 生成 vs Prompt 处理的分水岭 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 生成阶段(Generation / Decode) │
│ Batch = 1(逐 token 生成) │
│ 1 < 32 → CPU 执行,从 RAM @ 51 GB/s 读取 │
│ → 这就是你的 17 t/s │
│ │
│ Prompt 处理阶段(Prefill) │
│ Batch = 数百 tokens 同时处理 │
│ 数百 > 32 → llama.cpp 把专家权重经 PCIe 复制到 VRAM │
│ → 交给 GPU 计算 @ 192 GB/s │
│ → 同模型、同 flag,底层完全是另一台机器 │
│ │
└─────────────────────────────────────────────────────────────┘
优化手段:提高 microbatch 尺寸。默认 512 tokens,提升到 2048 后效果惊人:
| Microbatch | Prompt 处理速度 |
|---|---|
| 128 | 22 t/s |
| 512(默认) | ~100 t/s |
| 2048 | 345 t/s(15x 提升) |
2025 年 8 月的一个 patch 进一步优化:标题就叫「Copy only the used experts when offloading prompt processing」——之前所有 256 个专家都会被复制到 VRAM,现在只复制实际被激活的。
混合注意力架构大幅节省 KV Cache¶
Qwen3.6-35B-A3B 的 40 层结构:
| 层类型 | 层数 | 注意力机制 | KV Cache 行为 |
|---|---|---|---|
| Full Attention | 10 层(25%) | 传统注意力 | Cache 随 token 增长 |
| Gated DeltaNet(线性注意力) | 30 层(75%) | 固定大小状态 | 无增长,固定 state |
KV Cache 记忆体需求对比(每 token 每序列):
Qwen3.6-35B-A3B(混合注意力)
20,480 bytes/token → 262K 上下文 ≈ 5 GB
传统全注意力 MoE(同级别)
~98,000 bytes/token → 262K 上下文 ≈ 24 GB
差距:4.8x
「最便宜的 context,是架构本身从不分配的那些。」
TurboQuant:3-bit KV Cache 压缩(实验性)¶
TurboQuant(Google Research + NYU,arXiv 2025-04,ICLR 2026)通过旋转变换 + 单一 codebook 量化实现 3-bit KV Cache:
- 效果:KV 记忆体减少 6 倍,benchmark 分数不变
- 实测:6GB 卡上下文从 64K → 256K tokens
但目前在 llama.cpp 中不可用:
| 状态 | 详情 |
|---|---|
| 主线合并 | ❌ 所有 PR 均 closed/unmerged(共 75 个相关 PR/issue) |
| Fork 需求 | 需构建落后主线 300+ commits 的 fork |
| AI 政策标记 | 其中 2 个 PR 被标记「AI policy violation」 |
| 速度倒退 | 主线重写 MoE attention kernel 后,plain 16-bit 快了 4x,TurboQuant 路径未跟进;Turbo 2 解码速度仅剩 F16 的 45% |
结论:TurboQuant 是「省显存但牺牲速度」的取舍,当前阶段不建议追。
四、技术陷阱与极限瓶颈¶
瓶颈 1:记忆体频宽是最终天花板¶
┌──────────────────────────────────────────────┐
│ 记忆体频宽对比(决定速度上限) │
├──────────────────────────────────────────────┤
│ │
│ GPU VRAM(GTX 1060) 192 GB/s ████████ │
│ 系统 RAM(DDR4-3200) 51 GB/s ██ │
│ │
│ 专家权重一旦移到 RAM,速度由 RAM 频宽决定 │
│ 51/192 ≈ 1/4,这正是速度差距的来源 │
│ │
│ → 这个瓶颈没有任何 flag 能突破 │
└──────────────────────────────────────────────┘
不同硬件的速度上限参考:
| 硬件 | VRAM | 速度 | 备注 |
|---|---|---|---|
| GTX 1060 | 6GB | 17 t/s | 专家在 RAM |
| GTX 1070 | 8GB | ~18 t/s | @ 132K 上下文 |
| RTX 3090 | 24GB | 105 t/s | 全部塞进 VRAM |
| RTX 5090 | 32GB | 183 t/s | 全部塞进 VRAM |
瓶颈 2:推测解码(Speculative Decoding)在 MoE 上失效¶
推测解码的原理:用小模型(Draft Model)一次预测多个 token,大模型一次性验证。在 Dense 模型上接近免费加速。
在 MoE 上彻底失败,原因很优雅但令人恼火:
┌────────────────────────────────────────────────────┐
│ 为什么 Speculative Decoding 在 MoE 失效 │
├────────────────────────────────────────────────────┤
│ │
│ 正常生成 1 个 token: │
│ 唤醒 8 个专家 → 8 次记忆体读取 │
│ │
│ Speculative 预测 4 个 token: │
│ 唤醒的专家数 ≈ 大部分(非 8x4=32,而是去重后 │
│ 接近全量,因为不同 token 路由到不同专家) │
│ │
│ Mixtral 实测(2025 论文): │
│ 1 token → 2 个专家 │
│ 8 tokens → 7+ 个专家(3.5x 数据搬运) │
│ │
│ 专家在 CPU → 每个额外专家 = 多走一次记忆体汇流排 │
│ 验证阶段速度下降 2-3x │
│ 整体比不开 Speculative 还慢 1.5x │
│ │
└────────────────────────────────────────────────────┘
替代方案:多 token 预测(Multi-Token Prediction),将 draft head 训练进模型本身,共享 context 和 cache:
| 模型类型 | 加速比 |
|---|---|
| Dense 模型 | 1.4-2.0x |
| MoE 模型 | 1.15-1.25x(聊胜于无) |
稀疏性一手给、一手收——MoE 省了推理带宽,但也让投机解码失去了舞台。
五、4-bit 量化质量评估¶
22GB 的 4-bit 版本是否还是那个 SWE-bench 73.4 分的模型?
| 指标 | 4-bit | 8-bit | 说明 |
|---|---|---|---|
| KL Divergence | 0.0137 | 0.0026 | 4-bit 漂移是 8-bit 的 5 倍 |
| Perplexity 变化 | <1% | 更小 | 量化影响有限 |
Unsloth 自己的警告值得记住:Perplexity 和 KL Divergence 都受校准数据影响,都不能可靠预测模型能否完成你的具体任务。
最终的 benchmark 是在你自己的代码仓库上跑一天。
六、总结与落地建议¶
核心洞察¶
过去两年的规则是:VRAM 是一堵墙,模型要么装得进要么装不进,唯一的解法是买更大的卡。稀疏模型把这堵墙变成了预算。
┌──────────────────────────────────────────────────────────┐
│ 范式转变 │
├──────────────────────────────────────────────────────────┤
│ │
│ 旧范式:VRAM = 容量墙 │
│ "模型 fit 或不 fit,不 fit 就买卡" │
│ │
│ 新范式:VRAM = 预算分配 │
│ "哪些权重值得放进快速记忆体?" │
│ → 高频权重 → GPU VRAM │
│ → 低频权重 → 系统 RAM │
│ │
│ 6GB + 2016 年的卡 + 350 亿参数 + 实时响应 │
│ │
└──────────────────────────────────────────────────────────┘
适用 vs 不适用场景¶
┌─ 17 t/s > 人类阅读速度 ──→ ✅ 个人互动助手
│ ✅ 代码阅读 / 辅助工具
│ ✅ 桌面级日常对话
适用?──┤
│ ❌ 全自动 Agent 任务
└─ 17 t/s 对无人值守太慢 ─→ ❌ 大规模异步批处理
❌ 需要 API 级速度的场景
实践步骤¶
- 下载 4-bit MoE 模型(如 Q4_K_M),Qwen3.6-35B-A3B 的 Unsloth 构建为 22.36 GB
- 使用 llama.cpp 原生 CLI(不用 Ollama / LM Studio GUI)
- 用
--n-cpu-moe压低专家层,渐进调到 VRAM 填满前临界值 - 配合
-ngl把常规图层推回 GPU - 提高 microbatch(
-tb)兼顾 Prompt 读取速度 - 长期运行加
--mlock防止 page out
决策树:选哪种方案¶
你的 VRAM 够装下整个模型吗?
├─ 是 → 直接 -ngl 99,全部 GPU,不需要 --n-cpu-moe
└─ 否 → 模型是 MoE 架构吗?
├─ 否 → 降量化精度(Q3/Q2)或换更小模型
└─ 是 → 使用 --n-cpu-moe + -ngl 组合
├─ 模型 < 系统 RAM → --load-mode mlock(全量载入)
└─ 模型 > 系统 RAM → --load-mode mmap(保留映射)
参考资料¶
- Running a 22GB AI Model on a 6GB GPU, FAST (llama.cpp Guide) — Cloud Codes
- New llama.cpp options make MoE offloading trivial:
--n-cpu-moe— r/LocalLLaMA - Guide to optimizing inference performance of large MoE models — GitHub Gist
- Hybrid CPU/GPU inference — ik_llama.cpp docs
- Running MoE models with llama.cpp — ダフネ
相关笔记¶
- [[llama-cpp 本地 GGUF 推理]]
- [[本地 LLM 部署优化]]
文档生成时间:2026-08-01
基于 Cloud Codes 视频 + llama.cpp --n-cpu-moe 主线(2025-08 合并)