2026 年上半年,阿里发布了 Qwen3.5 系列模型,社区也涌现了不少基于它的微调版本。我在 RTX 4060 Laptop(8GB 显存)笔记本上用 llama.cpp 部署了 Qwen3.5-4B 和 Qwopus3.5-9B-Coder 两个模型,测试了 64K 上下文的实际表现。这篇文章整理从选型、配置到性能测试的完整过程。

硬件环境

组件 规格
CPU i7-12650H(16 线程)
RAM 15 GiB
GPU RTX 4060 Laptop(8GB VRAM,compute 8.9)
llama.cpp build 1,CUDA 后端

模型选择

两个目标:一个轻量模型做日常问答,一个专用模型做代码生成。

Qwen3.5-4B

阿里巴巴于 2026 年 2 月发布,Apache 2.0 协议。核心特点:

  • 4B 参数,Dense 架构
  • 采用 Gated Delta Networks + Gated Attention 混合架构,具体为 8×(3×DeltaNet→FFN→1×Attention→FFN)
  • 32 层,hidden_dim=2560,16 attention heads,4 KV heads(GQA)
  • 原生 262K 上下文,可扩展至 1M
  • 支持 MTP(Multi-Token Prediction),包含 1 个预测头
  • Q4_K_XL 量化后仅 2.85 GiB

选择理由:参数少、显存占用低、支持 MTP,适合做低延迟的实时交互场景。

Qwopus3.5-9B-Coder-MTP

社区基于 Qwen3.5-9B 架构的编程特化版本:

  • 9B 参数
  • 使用 Trace Inversion 数据增强和高质量 Agent Traces 微调
  • 社区添加了 MTP 层,作者在 7900 XTX 上实测从约 80 t/s 提升到约 120 t/s
  • 原生 32K 上下文,可扩展到 128K/256K
  • Q4_K_M 量化后 5.51 GiB

选择理由:代码生成质量高、MTP 加速、显存刚好能装下。

llama-bench 基准测试

先用 llama-bench 跑标准参数,得到纯推理速度的基线:

1
llama-bench -m model.gguf -ngl -1 -p 512 -n 128
测试项 4B GPU 4B CPU 加速比 9B GPU 9B CPU 加速比
Prompt 处理 (pp512) 2,998 t/s 797 t/s ×3.8 1,994 t/s 702 t/s ×2.8
文本生成 (tg128) 69.0 t/s 10.0 t/s ×6.9 43.8 t/s 6.9 t/s ×6.3

GPU 模式下的生成速度差了约 7 倍,CPU 跑 10 t/s 以下交互体验会比较卡。

llama-server 配置优化

llama-bench 只是理论速度,实际使用需要启动 llama-server。以下是我的最终配置。

4B 模型(端口 45380)

1
2
3
4
5
6
7
8
9
10
11
12
llama-server \
-m ./model/Qwen3.5-4B-UD-Q4_K_XL.gguf \
-ngl -1 \
-c 65536 \
-ctk q8_0 -ctv q8_0 \
--spec-type draft-mtp \
--spec-draft-type-k q8_0 --spec-draft-type-v q8_0 \
-fa on \
-t 8 \
-np 1 \
-b 4096 -ub 1024 \
--host 127.0.0.1 --port 45380

9B 模型(端口 45381)

1
2
3
4
5
6
7
8
9
10
11
12
llama-server \
-m ./model/Qwopus3.5-9B-Coder-MTP-Q4_K_M.gguf \
-ngl -1 \
-c 65536 \
-ctk q8_0 -ctv q8_0 \
--spec-type draft-mtp \
--spec-draft-type-k q8_0 --spec-draft-type-v q8_0 \
-fa on \
-t 8 \
-np 1 \
-b 4096 -ub 1024 \
--host 127.0.0.1 --port 45381

参数说明

参数 作用
-ngl -1 所有层加载到 GPU
-c 65536 64K 上下文窗口
-ctk / -ctv q8_0 KV cache 用 8-bit 量化,每条 token 约 27.5KB 开销
--spec-type draft-mtp 启用 MTP 推测解码
--spec-draft-type-k/v q8_0 MTP 草稿模型的 KV cache 也量化(默认 f16,显性设能省显存)
-fa on 强制启用 Flash Attention,减少中间缓冲
-t 8 8 个 CPU 线程(16 线程的 i7 用一半,留余量给系统)
-np 1 单槽位,避免多 slot 抢占上下文

MTP 草稿 cache 默认是 f16,不显式声明会浪费不少显存。这是实际测试中发现的第一个坑。

实际文本性能测试

用 llama-server 的 /completion API 跑了三类场景,每个场景先预热一次再计时。

4B 模型

测试 Prompt PP t/s 生成 TG t/s MTP 接收 耗时
简短问答 8 tok 121.4 128 tok 84.5 76/153 (50%) 1.6s
代码生成 26 tok 379.7 256 tok 100.9 170/252 (67%) 2.6s
长上下文 552 tok 2,306.1 256 tok 93.6 164/271 (61%) 3.0s
平均 - 935.7 - 93.0 ~68% -

9B Code 模型

测试 Prompt PP t/s 生成 TG t/s MTP 接收 耗时
简短问答 8 tok 63.3 128 tok 37.6 74/157 (47%) 3.6s
代码生成 26 tok 118.9 256 tok 48.8 174/238 (73%) 5.5s
长上下文 552 tok 1,235.1 256 tok 43.5 166/262 (63%) 6.3s
平均 - 472.4 - 43.3 ~69% -

MTP 加速效果

与 llama-bench 的纯推理速度对比,MTP 在实际应用中带来了明显的提升:

模型 llama-bench 实际加速后 提升
4B 69.0 t/s 93.0 t/s +35%
9B 43.8 t/s 43.3 t/s

4B 提升显著,9B 持平的原因是 llama-server 基础开销更大,MTP 主要用于抵消这部分损失。MTP 接受率约 68-69%,即每个 draft 周期预测 3 token 约命中 2 个。

64K 上下文显存分析

通过对比不同 -c 设置下的 VRAM 占用(启动后不跑任何请求),量化了各组件的分配:

4B + q8_0 cache

上下文长度 VRAM 占用 增量
4K 3,545 MiB -
16K 3,883 MiB +338 MiB
32K 4,381 MiB +498 MiB
64K 5,377 MiB +996 MiB

每 token KV 开销约 27.5 KiB(q8_0)。

9B + q8_0 cache

上下文长度 VRAM 占用 增量
4K 5,711 MiB -
16K 5,879 MiB +168 MiB
32K 6,233 MiB +522 MiB
64K 6,554 MiB +843 MiB

每 token KV 开销约 13.7 KiB。

VRAM 分布(64K 上下文)

组件 4B + q8_0 9B + q8_0
模型权重 + MTP 头 ~3,433 MiB ~5,711 MiB
KV cache(64K) ~1,832 MiB ~914 MiB
CUDA 缓冲 / 工作区 ~112 MiB ~-71 MiB (fitting 压缩)
总计 5,377 MiB 6,554 MiB
空闲 2,459 MiB 1,281 MiB

KV cache 是预分配的

一个值得注意的发现:llama.cpp 的 KV cache 是启动时一次性预分配的,而非按需动态增长。

验证方法:启动 64K 上下文的服务器,分别发送 0 token、500 token、10K token 的请求,VRAM 始终保持 5,377 MiB 不变(±30 MiB 测量误差)。如果用 -c 4096 启动,VRAM 直接降到 3,545 MiB。

换句话说,-c 的值直接决定显存占用,和实际用了多少上下文无关。这个设计的代价是启动时就必须留出全部缓存空间,好处是运行期间不会有内存碎片或 OOM 的风险。

踩坑记录

这里记录几个实际调试中遇到的问题。

坑一:MTP draft cache 默认是 f16

默认配置下 MTP 草稿模型的 KV cache 为 f16,比 q4_0/q8_0 精度的草稿缓存多用不少显存。必须显式设置 --spec-draft-type-k--spec-draft-type-v

坑二:n_parallel=auto 反而省显存

-np auto(自动判断为 4 slots + unified KV)在某些配置下比 -np 1 的 VRAM 更低,因为 fitting 算法在多头共享缓存池时会更积极压缩工作缓冲区。实际用户场景建议还是设 -np 1

坑三:9B 模型的 Gated Delta Net 警告

启动 9B 模型时出现 fused Gated Delta Net not supported,这个层被迫跑在 CPU 上。不影响正常使用,但说明 CUDA 后端对该架构的支持还不完善。

坑四:显存够用时 q8_0 和 q4_0 的差异不大

实测 9B 模型 q4_0(6,624 MiB)和 q8_0(6,554 MiB)的最终 VRAM 几乎一样,因为 llama.cpp 的 fitting 算法会自适应调节工作缓冲区。有显存余量就上 q8_0,精度更好。

总结

结论 数据
8GB 显存能跑 9B + 64K 上下文 6,554 MiB / 7,834 MiB
4B 生成 93 t/s 响应几乎无延迟
9B 代码生成 43 t/s 编码对话完全流畅
MTP 实际加速 30-50% 接受率 ~68%
KV cache 预分配 -c 设置决定一切

两个模型定位清晰:4B 做日常快速问答,9B Coder 做代码生成。8GB 显存虽然只能同时跑一个,但切换成本很低,配合不同的 API 端口可以随时切换。

核心教训是 MTP 的几个配置项(草稿 cache 量化、n_max、draft 预测头)对实际体验影响很大,值得花时间调。llama.cpp 的 fitting 算法也是一把双刃剑——它帮你省显存,但也让调参的反馈变得不直观,建议每次只改一个参数做 A/B 对比。