古董机器也能跑顶尖大模型
双路 Intel Xeon E5-2696 v4 + NVIDIA GeForce RTX 3060 12 GiB,运行 Qwen3.8-Flash-Next(UD-Q4_K_XL)
双路老至强,一张 RTX 3060,再加上系统可用约 125 GiB 的内存。这就是这次运行 Qwen3.8-Flash-Next 的全部计算硬件。
在这台机器上,zLLM 已经跑通模型的完整 48 层文本推理,并接入常驻服务。驱动分配的锁页主存方案在最新回归中约为 6.0–6.4 token/s,完成了 50 次请求与 4 次长生成的零失败验证。最新同题同参的单请求 MTP 基线达到 13.158 token/s。
几 token 每秒,意味着回答可以逐步出现在屏幕上,也意味着长答案需要耐心。对一台显存只有 12 GiB 的老平台来说,真正值得展开的是:它怎样把远大于显存的权重用起来,又怎样从“成功生成一次”走到连续提供服务。
老平台的优势,是能装下足够多的内存
本次机器与模型配置如下,性能与验证结论均来自这台机器的接入记录。
| 项目 | 配置 |
|---|---|
| CPU | 双路 Intel Xeon E5-2696 v4 |
| GPU | NVIDIA GeForce RTX 3060,12 GiB 显存 |
| 系统内存 | 125 GiB RAM |
| 系统 | Fedora 44 |
| GPU 软件环境 | NVIDIA driver 610.57.04,CUDA 13.3 |
| CPU 与 GPU 连接 | PCIe 3.0 x16 |
| 模型 | Qwen3.8-Flash-Next,GGUF 架构名 qwen4exp |
| 权重 | unsloth/Qwen3.8-Flash-Next-GGUF,UD-Q4_K_XL 四分片 |
“古董”主要说的是这套双路 E5 平台。整机仍然依赖一张支持 CUDA 的 GPU,以及足够大的主存。标题中的“顶尖大模型”也不代表本文做过能力排名:这是一篇部署与推理工程实录,没有独立评测模型质量或量化后的能力损失。
仅模型的量化专家权重就有 77,017,907,200 字节,约 71.73 GiB,已经远超显存容量。不过,它采用 MoE,也就是混合专家结构:每层会从 512 个专家中选择 10 个参与当前计算,另有共享专家。
这给了老机器一个机会。全部专家需要有地方存放,但每一步只需把实际选中的专家交给 GPU。大内存承担容量,显存承担当前计算,两者通过 PCIe 配合。
12 GiB 显存怎样装配完整模型
我们把权重分成三种去向。
| 权重类型 | 存放与执行方式 |
|---|---|
| 非专家权重 | 以量化形式驻留显存 |
| 约 71.73 GiB 的专家权重 | 常驻主存,路由命中后按需上传显存 |
| 大型 PLE 表与 embedding | 读取当前需要的压缩行,再生成计算所需数据 |
每到一层,路由先选专家。显存缓存中已经有的直接复用,缺失的从主存上传,随后执行量化专家计算。专家始终保留 GGUF 的压缩编码,没有先全部展开成半精度浮点数。
这一步很关键。如果模型文件在磁盘上很小,运行时却把权重重新撑大,容量和传输上的优势就会丢掉。zLLM 为实际使用的 Q4_K、Q5_K、Q5_1、Q8_0 等格式实现了直接消费压缩权重的 CUDA 算子。
显存中的专家缓存也有固定预算。我们一次申请一块显存,在内部管理区间与引用,确保上一轮计算不再使用某段区域之后,才允许复用。早期依赖动态分配的实现能完成短输出,却会在 128、256 token 的生成中耗尽显存;固定预算让长输出的资源占用有了明确上限。
这里的“按需上传”是主存到显存,专家已经完整驻留主存。PLE 与 embedding 仍可能发生少量随机读盘,因此整个过程仍有磁盘访问。
跑出一次高分之后,真正的难题才出现
最初的单请求实验,速度从 4.770 token/s 逐步提高。消除缓存命中时的显存深复制、减少主存中转、优化量化读取,再让专家上传与计算重叠,都带来了收益。
我们还接入了 shared MTP:用一个额外的草稿层提出候选 token,再由完整的 48 层模型验证。候选被接受时,一轮验证可以推进多个位置;被拒绝时,则恢复相关状态与缓存。bench 入口的 MTP 循环已经跑通,但常驻 Node 服务的 MTP 仍在收敛,当前线上配置因此关闭 MTP。
早期测试记录如下。这些都是独立进程的一次测量,属于历史实验结果。
| 场景 | 输入 / 输出 token | 普通解码 token/s | MTP token/s |
|---|---|---|---|
| 代码题 | 42 / 128 | 10.779 | 12.708 |
| 代码题 | 42 / 256 | 10.682 | 11.239 |
| 中文聊天 | 43 / 256 | 11.371 | 11.238 |
12.708 token/s 的准确含义是:原始 42-token 模板 prompt、生成 128 token、专家缓存 5.25 GiB、MTP 置信度阈值 0.6、专家预取 0、prefill chunk 64、MTP 专家缓存 0.25 GiB,4 个草稿、Q8G64 KV、独立 bench 进程的一次 MTP 测量,接受率 97.1%。在同一配置、同一题目上,驱动 pinned 主存和草稿 pinned 驻留把结果刷新到 13.158 token/s,接受率 97.09%;verify 用时从 8.960 秒降到 8.662 秒,draft 用时从 0.758 秒降到 0.713 秒,上传字节数不变。这是当前最高纪录,仍然不是常驻服务速度,也不包含模型预读。
随后做的 MTP 复测使用了 59 token 的中文代码题提示,得到 8.32 token/s;提示词与 42 token 基线不同,接受率降到 74.6%,验证阶段上传约 126.7 GiB,因此不能与 12.708 做 A/B 比较。45 token 对齐提示的复测仍在进行中。
为什么接入新模型不需要重写整个引擎
Qwen3.8-Flash-Next 的结构很特殊:Hyper-Connection、GDN、PLE、QSA 稀疏注意力和 512 专家路由同时出现。zLLM 仍然能在较短时间内接入,原因是架构把“模型语义”与“设备执行”拆开了。新增模型主要完成三类代码:
- 描述模型规格:在
src/model_spec/qwen4exp.rs声明 48 层布局、张量形状、路由常量、QSA/PLE/HC 参数和 GGUF 元数据。 - 装配并实现模型算法:在
src/runtime/qwen4exp/mod.rs读取权重、实现前向结构;cpu.rs提供 CPU reference;cuda.rs和cuda_mtp.rs只组合该模型需要的 CUDA 算子与 MTP 流程。 - 接入运行入口:在配置和 Node 注册处增加
Qwen4ExpNodeModelConfig、Qwen4ExpCudaEngine,复用已有的 chat template、流式输出、取消、stop/EOS 和 scheduler 协议。
这次模型专属的 5 个文件合计约 2,228 行 Rust(规格 186 行、装配 588 行、CPU reference 565 行、CUDA 组合 693 行、MTP 196 行)。它们调用的是已有的 GGUF 读取、KV cache、CUDA context/stream、固定显存 arena、专家 LRU、量化 kernel、服务协议和生命周期管理;这些基础设施不需要为每个模型复制一份。具体来说,专家搬运与缓存位于 backend/cuda/expert.rs,Q4/Q5/Q8 packed kernel、attention、routing 和 tensor kernel 由共享模块提供。新模型增加的是“如何解释权重、如何排列计算”,而不是再造一套推理运行时。
这种拆分也让性能和正确性可以分开检验。CPU reference、张量格式对照、单算子 CUDA 测试和逐 token 输出一致性先回答“算得对不对”;固定 arena、分组 DMA、缓存策略和 MTP 草稿长度再用完整请求测量“跑得快不快”。一次 kernel microbenchmark 变快,不能直接合入主线;必须同时通过 reference 对拍、长输出稳定性和同输入端到端吞吐。13.158 token/s 与旧 12.708 的对比中,验收率、轮数和上传字节逐位一致,提升来自 pinned 源上的稳定 overlap 路径;注册普通堆内存的旧快路已经移除。常驻 Node MTP 仍要单独完成正确性与性能验收。
更大的转折发生在服务接入之后。独立进程生成一次就退出,常驻服务却要反复接收请求。运行到第三次、第五次,或者更晚,堆损坏才暴露出来。前一次已经把内存写坏,下一次分配内存时才崩溃,报错的位置未必就是出错的位置。
排查先修复了 PLE 读取时的列宽错误,随后又通过反复对照,把另一类损坏收窄到注册普通堆内存后进行异步 DMA 的直读路径。改用独立的锁页暂存槽后,连续请求通过了,但每次上传多了一次 CPU 内存复制,服务解码从约 6.4 降到了约 4.2 token/s。
最后保留的方案,是让专家权重直接读入 CUDA 驱动分配的锁页主存,并由运行时持续持有这块内存,再从中直接上传 GPU。它消除了额外的主存复制,也避开了原来的普通堆注册路径。
| 常驻服务的专家上传方案 | 解码速度 | 验证结果 |
|---|---|---|
| 旧的注册堆直读 | 约 6.4 token/s | 连续请求发生堆损坏 |
| 锁页暂存槽中转 | 约 4.2 token/s | 修复后 30 次请求通过 |
| 驱动分配的锁页主存直读 | 约 6.0–6.4 token/s | 50 次请求及 4 次长生成零失败 |
这组记录说明了服务路径上的性能恢复。它与前面的短提示 benchmark 配置、执行入口不同,不能直接用来计算性能回退比例。修复后的版本仍需建立同条件的 MTP 性能与正确性基线。
锁页内存还有一个运维细节:进程正常退出后,cuMemFreeHost 会把约 95 GiB 交还给驱动的 host 池,操作系统的 MemAvailable 不会立刻恢复;下次分配可以复用,但当前的内存预检可能误拒运行。强制 SIGKILL 则可能留下 NVIDIA 驱动记账泄漏,最终需要重启。部署时应使用可执行清理逻辑的优雅退出,并避免用 pkill -9 回收节点。
限制速度的,是每个 token 要搬多少权重
大内存解决了“放在哪里”,接下来就要支付搬运成本。
早期代码题的 256-token MTP 测试中,每个输出 token 平均对应约 632.5 MB 的专家上传。这台机器的锁页传输探针测得 11.90 GB/s,按该带宽计算,仅搬运这些数据就需要约 53.2 毫秒/token。
20 token/s 的总时间预算只有 50 毫秒/token。仅传输就已经超过预算,还没有算上 GPU 计算、草稿生成和同步。这解释了为什么当时没有达到 20 token/s,也解释了为什么继续增加草稿长度没有持续提速。
这笔账针对当时的配置,不能当作最新服务的耗时分解。但它指出了后续方向:提高有用专家的复用,减少重复上传,让搬运与计算更充分地重叠。GPU 利用率再高,也无法抵消关键路径上必须等待的数据传输。
实际使用,还要看首字等待与服务回收
解码速度描述的是开始生成之后的速度。长材料送进模型,首先要经过 prefill,也就是输入处理。接入稀疏 QSA 注意力后,记录中约 10.6K token 的输入处理耗时为 351.2 秒。因此,这台机器处理长文档时,首字等待可能以分钟计。
记录中的 65,536 是经过测试的上下文配置上限;它不能被解读为已经完成 65,536-token 满长输入测试。更长的上下文仍有待验证,视觉能力也尚未接入。
最新锁页主存方案还有一个实际运维问题:在该环境中,强制杀死持有大量锁页内存的进程后,出现过驱动未回收内存的现象,最终需要重启机器恢复。常规回收应采用能够执行资源释放的退出流程;原记录尚未验证优雅退出是否能完全消除这一问题。
正确性证据同样有边界。早期 CUDA 输出与自有 CPU reference 做过对照,代码题和中文聊天的 256-token MTP 输出也曾与普通解码逐 token 相同。但独立 llama.cpp 对照在第五个 token 出现分歧,完整 logits 对齐尚未完成。这些验证支持已测场景的工程判断,不能替代模型质量评测。
老机器的价值,被软件重新挖出来了
这次接入最有意义的结果,是把一台双路 E5、125 GiB 内存、12 GiB 显存的机器,变成了能够运行完整 Qwen3.8-Flash-Next 文本模型的推理节点。
代价也很具体:专家占用约 77 GB 主存,PCIe 限制生成速度,长输入有明显等待,服务退出还需要继续验证。当前约 6.0–6.4 token/s 的服务记录,与单请求 MTP 的 13.158 token/s 属于不同入口和口径。
老平台的大内存、消费级显卡的计算能力,以及量化模型的稀疏结构,能够在合适的执行方式下配合起来。把每份权重放到合适的位置,保持压缩编码,管理好异步传输的生命周期,这些软件工作让已有硬件承担了原本看起来超出容量的任务。
本文依据 zLLM《接入 Qwen3.8-Flash-Next 实录》及其 2026-09-07 后续记录整理。未重新运行 benchmark;历史单请求成绩与最新服务验证分别列示。
