参悟llama.cpp中的Qwen3.6-35B-A3B
参悟llama.cpp中的Qwen3.6-35B-A3B
懒人版:直接复制给LLM,“总结一下这篇文章讲了什么”
前言
这篇文章既是对一系列探究性实践的记录与总结,也是深入理解 MoE(Mixture of Experts)架构底层逻辑与 llama.cpp 部署机制的学习过程。
模型: Qwen3.6-35B-A3B
框架: llama.cpp (Build: 91c631b21)
硬件: 1 x RTX 3090 (24GB, 936 GB/s), 1 x Tesla T4 (16GB)
Qwen3.6-35B-A3B: https://www.modelscope.cn/models/Qwen/Qwen3.6-35B-A3B
llama.cpp: https://github.com/ggml-org/llama.cpp
1. 疑问
1.1 什么是MOE?
MoE 的本质是将一个巨大的神经网络拆解为多个独立的子网络(专家,Expert),并配有一个“路由器”。
- 路由器:分析输入 Token,决定哪几个专家(通常 2-8 个)最适合处理当前上下文。
- 专家:彼此独立,擅长特定领域(如代码、数学)。
- 稀疏激活:推理时,只有被选中的专家参与计算。对于 Qwen3.6-35B-A3B,总参数 35B,但每个 Token 仅激活约 3B 参数。
以Qwen3.6-35B-A3B为例,
参数总量:共 35B,其中激活参数为 3B
隐藏层维度:2048
Token 嵌入:248320(已填充)
层数:40
隐藏层结构:10 × (3 × (门控 DeltaNet → MoE) → 1 × (门控注意力 → MoE))
门控 DeltaNet:
- 线性注意力头数量:V 为 32,QK 为 16
- 头维度:128
门控注意力: - 注意力头数量:Q 为 16,KV 为 2
- 头维度:256
旋转位置嵌入维度:64
混合专家(Mixture Of Experts) - 专家总数:256
- 激活专家数:8 个路由专家 + 1 个共享专家
- 专家中间层维度:512
忽略潜入层、预测层和MTP层等一次性层,只考虑循环了40层的(注意力+MoE)结构,Qwen3.6-35B-A3B的参数比例可以粗略估计:
设40层注意力的总和参数规模为X,40层路由专家的总和参数规模为256Y,40层共享专家的总和参数规模为Z,则有:
- X + 256Y + Z = 35B
- X + 8Y + Z = 3B
可以推算出:
- Y = (35B - 3B) / (256 - 8) ≈ 4/31 B = 0.129 B
- X+Z = 1.9677 B
- 路由专家占据了 256Y 约等于 33.03B,留给其他层的容量只有不到2B。
以上为纯以理解为目的的粗略估算,精准数值还是要认真学习对应架构、知识、源码才能算得出来。
1.2 为什么MOE推理速度快?
由于MoE架构中只有部分专家参与计算,因此可以显著减少每次推理时需要处理的参数量,从而提高推理速度。
以3B为例,就算以fp16精度进行计算,完成一个token的推理也只需要处理约6GiB的数据,在3090 35.6 TFLOPS的算力下,理论上可以在不到1ms内完成一个token的推理。真正的算力瓶颈反而是其936 GB/s的显存带宽,其推理速度的物理上限就是 936/6 ≈ 156 T/s,而现实中还得考虑推理框架的效率、算力调度等因素,实际速度一定会明显低于这个上限。
但是理论物理上限仍然高于同系列的Qwen3.6-27B模型,后者的参数量为27B,激活参数量为27B,fp16精度下每个token的推理需要处理约54GiB的数据,3090的显存带宽936 GB/s下,理论物理上限为 936/54 ≈ 17 T/s,远低于Qwen3.6-35B-A3B的156 T/s。
1.3 MOE的显存占用更小吗?
取决于上下文长度和注意力层的设计。
当上下文很短的时候,Qwen3.6-35B-A3B的显存占用反而大于Qwen3.6-27B。这是因为显存占用主要由模型权重+KVCache+中间激活值组成,而KVC的占用量与上下文长度成正比,注意力层的设计也会影响KVC的占用量。
当上下文很长的时候,Qwen3.6-27B的显存占用会超过Qwen3.6-35B-A3B,因为Qwen3.6-35B-A3B的注意力层参数规模远小于Qwen3.6-27B,KVC的占用量也远小于Qwen3.6-27B。此时KVCache成为了Qwen3.6-27B的主要显存占用来源,而Qwen3.6-35B-A3B即使在原生最大的256K上下文下,KVCache的占用量也只有约6.25GiB,相比于总权重数据量大35GiB不值一提。
门控注意力的KV Cache大小 (字节) = 层数 × 序列长度 × 每个 Token 的 KV 维度 × 2(K和V) × 字节数
30 * 262144 * 128 * 2 * 2 + 10 * 262144 * 256 * 2 * 2 = 6,710,886,400 字节 ≈ 6.25 GiB
1.4 llama.cpp对MOE有哪些支持?
- 支持运行高度量化的MoE模型(q4_0, q8_0等)。
- ncmoe N:将前 N 层的 MoE 权重卸载到 CPU。N=0 全在 GPU,N=40 全部在 CPU。
- ctk / -ctv:控制 KV Cache 的量化精度(f16, q8_0, q4_0)。
- ngl:控制加载到 GPU 的层数(对于 MoE,通常设为 999 让 llama.cpp 自动管理,或通过 ncmoe 精细控制)。
1.5 1张3090能跑Qwen3.6-35B-A3B吗?
直接下载usloth量化的Qwen3.6-35B-A3B-UD-Q4_K_M.gguf,其大小为22.13GB,显然1张3090理论上刚好能装下。但如果你真的去部署就发现会OOM,因为:
- 显存占用不仅仅是模型权重,还包括KV Cache和中间激活值。
- llama.cpp的固有开销。
- 预留显存给系统和其他程序。
那么就只能尝试更高强度的量化,例如Qwen3.6-35B-A3B-UD-Q2_K_XL.gguf(12.29GB)或者Qwen3.6-35B-A3B-UD-IQ4_NL.gguf(18.04GB)。
一般来说,Q4_K_M量化能去的质量与性能的平衡,强度高于q4都会显著损失性能,强度低于q4则显存占用会显著增加。
因此可以尝试IQ4_NL量化,或者,迎难而上,从其他角度解决这个问题。
2. 理解
2.1 个人部署大模型关注的性能指标
个人使用大模型往往用于Chat和Agentic Engineering,往往只关注3个指标:
- 显存占用:显存占用越小,部署的灵活性越高。
- 上下文长度:上下文长度越长,模型的上限就越高。
- 推理速度:推理速度越快,交互体验越好。
2.2 影响显存占用的因素
- 模型参数量:参数量越大,显存占用越高。(这一项没有办法通过工程手段控制)
- 权重量化精度:量化精度越低,显存占用越小。(这一项没有办法通过工程手段控制)
- 上下文长度:上下文长度越长,KV Cache的显存占用越高。
- 中间激活值:中间激活值越多,显存占用越高。(这一项没有办法通过工程手段控制)
这就让我们陷入了一个矛盾,如果我们想要更长的上下文长度,就必须要有更大的显存占用,但是我们想要更小的显存占用,就必须要有更短的上下文长度。
幸好,llama.cpp提供了ctk/ctv参数来控制KV Cache的量化精度,从而在一定程度上缓解了这个矛盾。
2.3 影响推理速度的因素
- 模型激活参数量:激活参数量越大,推理速度越慢。(然而这一项只由模型作者决定)
- GPU的计算精度支持:原生支持的精度越低,浪费在反量化上的时间越少,推理速度越快。(3090只支持fp16和int8,且算力已经溢出)
- GPU的显存带宽:显存带宽越高,推理速度越快,这是第一个内存墙。(3090只有936 GB/s,成为瓶颈)
- CPU的计算精度支持:可惜,CPU最低通过AVX2支持fp16,推理速度远低于GPU,但服务器级别的CPU算力性能对于“A3B”来说也溢出了。
- CPU RAM的带宽:这是第二个内存墙,CPU RAM的带宽远低于GPU显存带宽,成为瓶颈。
有时候,我们不得不卸载部分权重和计算到CPU上。
3. 采样实验
太多了,可以直接跳到第4章。
对于我来说,理论分析最大的障碍不是知识本身,而是缺乏实践导致的就算完成了理论推算,也不敢相信自己推算的正确性。
这种心理往往会让人陷入精神内耗,而教员的实践论说了,想不通就先干。
为了用1张3090部署256K上下文的Qwen3.6-35B-A3B,有两种潜在的方向:
- 卸载部分权重和计算到CPU上,降低GPU显存占用,llama.cpp的-ncmoe参数可以做到这一点。
- 降低KV Cache的量化精度,降低GPU显存占用,llama.cpp的-ctk和-ctv参数可以做到这一点。
3.1 不同配置下的显存占用测试
用llama-server不断在不同的配置下部署模型,并在推理过程中观察显存占用情况,记录如下:
| 序号 | KV类型 | ncmoe | 上下文长度 | 已用显存 | 总显存 |
|---|---|---|---|---|---|
| 1 | f16 | 0 | 8192 | 21386MiB | 24576MiB |
| 2 | f16 | 0 | 16384 | 21554MiB | 24576MiB |
| 3 | f16 | 0 | 32768 | 21890MiB | 24576MiB |
| 4 | f16 | 0 | 65536 | 22562MiB | 24576MiB |
| 5 | f16 | 0 | 131072 | 23906MiB | 24576MiB |
| 7 | f16 | 1 | 8192 | 21078MiB | 24576MiB |
| 8 | f16 | 1 | 16384 | 21246MiB | 24576MiB |
| 9 | f16 | 1 | 32768 | 21582MiB | 24576MiB |
| 10 | f16 | 1 | 65536 | 22254MiB | 24576MiB |
| 11 | f16 | 1 | 131072 | 23598MiB | 24576MiB |
| 13 | f16 | 2 | 8192 | 20618MiB | 24576MiB |
| 14 | f16 | 2 | 16384 | 20786MiB | 24576MiB |
| 16 | f16 | 2 | 65536 | 21794MiB | 24576MiB |
| 17 | f16 | 2 | 131072 | 23138MiB | 24576MiB |
| 19 | f16 | 4 | 8192 | 19830MiB | 24576MiB |
| 20 | f16 | 4 | 16384 | 19998MiB | 24576MiB |
| 21 | f16 | 4 | 32768 | 20334MiB | 24576MiB |
| 22 | f16 | 4 | 65536 | 21006MiB | 24576MiB |
| 23 | f16 | 4 | 131072 | 22350MiB | 24576MiB |
| 25 | f16 | 8 | 8192 | 17974MiB | 24576MiB |
| 26 | f16 | 8 | 16384 | 18142MiB | 24576MiB |
| 27 | f16 | 8 | 32768 | 18478MiB | 24576MiB |
| 28 | f16 | 8 | 65536 | 19150MiB | 24576MiB |
| 29 | f16 | 8 | 131072 | 20494MiB | 24576MiB |
| 30 | f16 | 8 | 262144 | 23182MiB | 24576MiB |
| 31 | f16 | 16 | 8192 | 14264MiB | 24576MiB |
| 32 | f16 | 16 | 16384 | 14432MiB | 24576MiB |
| 33 | f16 | 16 | 32768 | 14768MiB | 24576MiB |
| 34 | f16 | 16 | 65536 | 15440MiB | 24576MiB |
| 35 | f16 | 16 | 131072 | 16784MiB | 24576MiB |
| 36 | f16 | 16 | 262144 | 19472MiB | 24576MiB |
| 37 | f16 | 32 | 8192 | 6840MiB | 24576MiB |
| 38 | f16 | 32 | 16384 | 7008MiB | 24576MiB |
| 39 | f16 | 32 | 32768 | 7344MiB | 24576MiB |
| 40 | f16 | 32 | 65536 | 8016MiB | 24576MiB |
| 41 | f16 | 32 | 131072 | 9360MiB | 24576MiB |
| 42 | f16 | 32 | 262144 | 12048MiB | 24576MiB |
| 43 | f16 | 64 | 8192 | 3058MiB | 24576MiB |
| 44 | f16 | 64 | 16384 | 3226MiB | 24576MiB |
| 45 | f16 | 64 | 32768 | 3562MiB | 24576MiB |
| 46 | f16 | 64 | 65536 | 4234MiB | 24576MiB |
| 47 | f16 | 64 | 131072 | 5578MiB | 24576MiB |
| 48 | f16 | 64 | 262144 | 8266MiB | 24576MiB |
| 49 | q8_0 | 0 | 8192 | 21312MiB | 24576MiB |
| 50 | q8_0 | 0 | 16384 | 21420MiB | 24576MiB |
| 51 | q8_0 | 0 | 32768 | 21638MiB | 24576MiB |
| 52 | q8_0 | 0 | 65536 | 22074MiB | 24576MiB |
| 53 | q8_0 | 0 | 131072 | 22946MiB | 24576MiB |
| 55 | q8_0 | 1 | 8192 | 21004MiB | 24576MiB |
| 56 | q8_0 | 1 | 16384 | 21096MiB | 24576MiB |
| 57 | q8_0 | 1 | 32768 | 21282MiB | 24576MiB |
| 58 | q8_0 | 1 | 65536 | 21654MiB | 24576MiB |
| 59 | q8_0 | 1 | 131072 | 22630MiB | 24576MiB |
| 61 | q8_0 | 2 | 8192 | 20544MiB | 24576MiB |
| 62 | q8_0 | 2 | 16384 | 20636MiB | 24576MiB |
| 63 | q8_0 | 2 | 32768 | 20822MiB | 24576MiB |
| 64 | q8_0 | 2 | 65536 | 21194MiB | 24576MiB |
| 65 | q8_0 | 2 | 131072 | 22170MiB | 24576MiB |
| 66 | q8_0 | 2 | 262144 | 23914MiB | 24576MiB |
| 67 | q8_0 | 4 | 8192 | 19756MiB | 24576MiB |
| 68 | q8_0 | 4 | 16384 | 19848MiB | 24576MiB |
| 69 | q8_0 | 4 | 32768 | 20034MiB | 24576MiB |
| 70 | q8_0 | 4 | 65536 | 20406MiB | 24576MiB |
| 71 | q8_0 | 4 | 131072 | 21238MiB | 24576MiB |
| 72 | q8_0 | 4 | 262144 | 22982MiB | 24576MiB |
| 73 | q8_0 | 8 | 8192 | 17900MiB | 24576MiB |
| 74 | q8_0 | 8 | 16384 | 17992MiB | 24576MiB |
| 75 | q8_0 | 8 | 32768 | 18178MiB | 24576MiB |
| 76 | q8_0 | 8 | 65536 | 18550MiB | 24576MiB |
| 77 | q8_0 | 8 | 131072 | 19382MiB | 24576MiB |
| 78 | q8_0 | 8 | 262144 | 21126MiB | 24576MiB |
| 79 | q8_0 | 16 | 8192 | 14190MiB | 24576MiB |
| 80 | q8_0 | 16 | 16384 | 14282MiB | 24576MiB |
| 81 | q8_0 | 16 | 32768 | 14468MiB | 24576MiB |
| 82 | q8_0 | 16 | 65536 | 14840MiB | 24576MiB |
| 83 | q8_0 | 16 | 131072 | 15672MiB | 24576MiB |
| 84 | q8_0 | 16 | 262144 | 17416MiB | 24576MiB |
| 85 | q8_0 | 32 | 8192 | 6766MiB | 24576MiB |
| 86 | q8_0 | 32 | 16384 | 6858MiB | 24576MiB |
| 87 | q8_0 | 32 | 32768 | 7044MiB | 24576MiB |
| 88 | q8_0 | 32 | 65536 | 7416MiB | 24576MiB |
| 89 | q8_0 | 32 | 131072 | 8248MiB | 24576MiB |
| 90 | q8_0 | 32 | 262144 | 9992MiB | 24576MiB |
| 91 | q8_0 | 64 | 8192 | 2984MiB | 24576MiB |
| 92 | q8_0 | 64 | 16384 | 3076MiB | 24576MiB |
| 93 | q8_0 | 64 | 32768 | 3262MiB | 24576MiB |
| 94 | q8_0 | 64 | 65536 | 3634MiB | 24576MiB |
| 95 | q8_0 | 64 | 131072 | 4432MiB | 24576MiB |
| 96 | q8_0 | 64 | 262144 | 6176MiB | 24576MiB |
| 97 | q4_0 | 0 | 8192 | 21272MiB | 24576MiB |
| 98 | q4_0 | 0 | 16384 | 21340MiB | 24576MiB |
| 99 | q4_0 | 0 | 32768 | 21478MiB | 24576MiB |
| 100 | q4_0 | 0 | 65536 | 21754MiB | 24576MiB |
| 101 | q4_0 | 0 | 131072 | 22306MiB | 24576MiB |
| 102 | q4_0 | 0 | 262144 | 23410MiB | 24576MiB |
| 103 | q4_0 | 1 | 8192 | 20964MiB | 24576MiB |
| 104 | q4_0 | 1 | 16384 | 21016MiB | 24576MiB |
| 105 | q4_0 | 1 | 32768 | 21122MiB | 24576MiB |
| 106 | q4_0 | 1 | 65536 | 21334MiB | 24576MiB |
| 107 | q4_0 | 1 | 131072 | 21990MiB | 24576MiB |
| 108 | q4_0 | 1 | 262144 | 23094MiB | 24576MiB |
| 109 | q4_0 | 2 | 8192 | 20504MiB | 24576MiB |
| 110 | q4_0 | 2 | 16384 | 20556MiB | 24576MiB |
| 111 | q4_0 | 2 | 32768 | 20662MiB | 24576MiB |
| 112 | q4_0 | 2 | 65536 | 20874MiB | 24576MiB |
| 113 | q4_0 | 2 | 131072 | 21530MiB | 24576MiB |
| 114 | q4_0 | 2 | 262144 | 22634MiB | 24576MiB |
| 115 | q4_0 | 4 | 8192 | 19716MiB | 24576MiB |
| 116 | q4_0 | 4 | 16384 | 19768MiB | 24576MiB |
| 117 | q4_0 | 4 | 32768 | 19874MiB | 24576MiB |
| 118 | q4_0 | 4 | 65536 | 20086MiB | 24576MiB |
| 119 | q4_0 | 4 | 131072 | 20598MiB | 24576MiB |
| 120 | q4_0 | 4 | 262144 | 21702MiB | 24576MiB |
| 121 | q4_0 | 8 | 8192 | 17860MiB | 24576MiB |
| 122 | q4_0 | 8 | 16384 | 17912MiB | 24576MiB |
| 123 | q4_0 | 8 | 32768 | 18018MiB | 24576MiB |
| 124 | q4_0 | 8 | 65536 | 18230MiB | 24576MiB |
| 125 | q4_0 | 8 | 131072 | 18742MiB | 24576MiB |
| 126 | q4_0 | 8 | 262144 | 19846MiB | 24576MiB |
| 127 | q4_0 | 16 | 8192 | 14150MiB | 24576MiB |
| 128 | q4_0 | 16 | 16384 | 14202MiB | 24576MiB |
| 129 | q4_0 | 16 | 32768 | 14308MiB | 24576MiB |
| 130 | q4_0 | 16 | 65536 | 14520MiB | 24576MiB |
| 131 | q4_0 | 16 | 131072 | 15032MiB | 24576MiB |
| 132 | q4_0 | 16 | 262144 | 16136MiB | 24576MiB |
| 133 | q4_0 | 32 | 8192 | 6726MiB | 24576MiB |
| 134 | q4_0 | 32 | 16384 | 6778MiB | 24576MiB |
| 135 | q4_0 | 32 | 32768 | 6884MiB | 24576MiB |
| 136 | q4_0 | 32 | 65536 | 7096MiB | 24576MiB |
| 137 | q4_0 | 32 | 131072 | 7608MiB | 24576MiB |
| 138 | q4_0 | 32 | 262144 | 8712MiB | 24576MiB |
| 139 | q4_0 | 64 | 8192 | 2944MiB | 24576MiB |
| 140 | q4_0 | 64 | 16384 | 2996MiB | 24576MiB |
| 141 | q4_0 | 64 | 32768 | 3102MiB | 24576MiB |
| 142 | q4_0 | 64 | 65536 | 3314MiB | 24576MiB |
| 143 | q4_0 | 64 | 131072 | 3792MiB | 24576MiB |
| 144 | q4_0 | 64 | 262144 | 4896MiB | 24576MiB |
3.2 不同配置下的推理速度测试
| model | size | params | backend | ngl | fa | test | t/s |
|---|---|---|---|---|---|---|---|
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | pp256 | 2597.45 ± 27.18 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | tg256 | 154.43 ± 0.34 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | pp512 | 3430.11 ± 33.18 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | tg512 | 153.56 ± 0.40 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | pp1024 | 3428.32 ± 19.55 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | tg1024 | 153.09 ± 0.28 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | pp2048 | 3389.95 ± 15.49 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | tg2048 | 151.92 ± 0.15 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | pp4096 | 3331.15 ± 18.29 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | tg4096 | 150.90 ± 0.27 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | pp8192 | 3246.80 ± 23.03 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | tg8192 | 149.02 ± 0.03 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | pp16384 | 3121.98 ± 11.93 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | tg16384 | 146.02 ± 0.22 |
| model | size | params | backend | ngl | type_k | type_v | fa | test | t/s |
|---|---|---|---|---|---|---|---|---|---|
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | pp256 | 2588.50 ± 42.67 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | tg256 | 151.48 ± 0.64 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | pp512 | 3441.18 ± 25.07 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | tg512 | 150.97 ± 0.16 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | pp1024 | 3392.41 ± 22.50 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | tg1024 | 149.47 ± 0.30 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | pp2048 | 3353.24 ± 15.10 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | tg2048 | 147.73 ± 0.42 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | pp4096 | 3271.68 ± 19.67 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | tg4096 | 145.95 ± 0.19 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | pp8192 | 3214.79 ± 20.45 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | tg8192 | 142.95 ± 0.15 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | pp16384 | 3075.39 ± 8.89 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q8_0 | q8_0 | 1 | tg16384 | 138.21 ± 0.07 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | pp256 | 2572.44 ± 31.25 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | tg256 | 149.69 ± 0.34 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | pp512 | 3408.31 ± 34.58 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | tg512 | 148.75 ± 0.23 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | pp1024 | 3382.18 ± 18.49 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | tg1024 | 148.04 ± 0.27 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | pp2048 | 3342.87 ± 17.04 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | tg2048 | 147.15 ± 0.16 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | pp4096 | 3276.92 ± 20.85 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | tg4096 | 145.38 ± 0.24 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | pp8192 | 3208.74 ± 21.82 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | tg8192 | 141.83 ± 0.16 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | pp16384 | 3065.99 ± 12.52 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | q4_0 | q4_0 | 1 | tg16384 | 136.11 ± 0.10 |
| model | size | params | backend | ngl | n_cpu_moe | fa | test | t/s |
|---|---|---|---|---|---|---|---|---|
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | pp256 | 1382.40 ± 40.33 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | tg256 | 143.65 ± 0.82 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | pp512 | 2101.12 ± 27.76 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | tg512 | 144.80 ± 0.25 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | pp1024 | 2111.58 ± 19.31 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | tg1024 | 137.42 ± 1.84 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | pp2048 | 2102.57 ± 18.04 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | tg2048 | 137.34 ± 2.13 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | pp4096 | 2084.55 ± 6.25 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | tg4096 | 140.47 ± 0.50 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | pp8192 | 2055.45 ± 8.94 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | tg8192 | 137.27 ± 0.39 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | pp16384 | 2001.76 ± 7.48 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | 1 | tg16384 | 136.56 ± 0.26 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | pp256 | 934.74 ± 16.93 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | tg256 | 132.00 ± 0.58 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | pp512 | 1502.21 ± 20.14 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | tg512 | 135.54 ± 0.33 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | pp1024 | 1506.38 ± 9.86 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | tg1024 | 135.84 ± 0.68 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | pp2048 | 1511.62 ± 16.53 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | tg2048 | 127.74 ± 0.48 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | pp4096 | 1492.47 ± 8.55 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | tg4096 | 125.46 ± 0.42 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | pp8192 | 1487.76 ± 7.11 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | tg8192 | 125.11 ± 0.53 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | pp16384 | 1469.13 ± 3.86 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 2 | 1 | tg16384 | 130.18 ± 0.23 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | pp256 | 574.18 ± 7.65 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | tg256 | 113.82 ± 1.83 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | pp512 | 954.11 ± 11.41 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | tg512 | 123.88 ± 1.11 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | pp1024 | 961.74 ± 7.78 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | tg1024 | 123.63 ± 0.21 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | pp2048 | 961.56 ± 4.45 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | tg2048 | 122.61 ± 0.38 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | pp4096 | 959.54 ± 2.39 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | tg4096 | 120.99 ± 0.42 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | pp8192 | 956.03 ± 2.05 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | tg8192 | 121.32 ± 0.41 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | pp16384 | 946.81 ± 2.85 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 4 | 1 | tg16384 | 114.45 ± 0.33 |
| model | size | params | backend | ngl | n_cpu_moe | type_k | type_v | fa | test | t/s |
|---|---|---|---|---|---|---|---|---|---|---|
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | pp256 | 1357.53 ± 67.94 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | tg256 | 140.76 ± 0.55 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | pp512 | 2105.04 ± 33.56 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | tg512 | 135.62 ± 1.49 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | pp1024 | 2110.99 ± 13.32 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | tg1024 | 137.20 ± 0.90 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | pp2048 | 2108.66 ± 8.02 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | tg2048 | 134.88 ± 0.72 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | pp4096 | 2083.96 ± 10.21 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | tg4096 | 138.41 ± 0.12 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | pp8192 | 2056.76 ± 6.87 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | tg8192 | 135.32 ± 0.12 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | pp16384 | 2004.78 ± 5.40 |
| qwen35moe 35B.A3B Q4_K - Medium | 20.60 GiB | 34.66 B | CUDA | 999 | 1 | q8_0 | q8_0 | 1 | tg16384 | 129.76 ± 0.12 |
4. 结论、推论、实践
4.1 近似模型
有了第三章采样的大量数据,我们可以先彻底忘记MoE架构,TransFormer理论,直接暴力拟合一个近似模型来预测显存占用和推理速度。
4.1.1 显存占用近似模型
其中:
例 1:配置 \(q = \text{q4\_0}\),\(n = 4\),\(L = 262144\)
实测值:21702 MiB,误差 \(\approx 0.6\%\)
例 2:配置 \(q = \text{f16}\),\(n = 8\),\(L = 65536\)
实测值:19150 MiB,误差 \(\approx 0.4\%\)
4.1.2 推理速度近似模型
其中:
| 符号 | 含义 | 单位 |
|---|---|---|
| \(\text{Decode}\) | Decode 阶段速度 | tokens/s |
| \(\text{CapDecode}\) | Decode 初始速度(\(L \to 0\) 时) | tokens/s |
| \(\gamma\) | Decode 速度衰减系数 | (tokens/s)/token |
| \(L\) | 上下文长度(实际处理的 token 数) | tokens |
| \(q\) | KV Cache 量化类型 | - |
| \(n\) | ncmoe 值 |
- |
Decode 参数
| \(q\) | \(n\) | \(\text{CapDecode}\) (t/s) | \(\gamma\) (\(\times 10^{-4}\)) |
|---|---|---|---|
| f16 | 0 | ~154.5 | ~1.1 |
| f16 | 1 | ~144.0 | ~0.9 |
| f16 | 2 | ~133.0 | ~0.8 |
| f16 | 4 | ~116.0 | ~0.6 |
| q8_0 | 0 | ~152.0 | ~1.5 |
| q8_0 | 1 | ~141.0 | ~1.2 |
| q4_0 | 0 | ~150.0 | ~1.8 |
| q4_0 | 1 | ~140.0 | ~1.5 |
4.2 近似模型的自回归测试
目前llama.cpp的显存占用在部署完成之后就基本稳定不变了,但仍然可能存在1GB以内的波动(经验数据)。所以,在选择配置时,建议预留至少1GB的显存空间,以避免OOM错误。
然后就是规划的顺序,优先考虑显存,然后考虑速度。在质量方面可以以显存为主,毕竟没有使用q4_0以上的量化等级,根据社区经验来讲,质量损失不会太大,只需尽量使用较低强度的量化即可。
仍然是256k上下文,那么就有
可行解有:
| n | \(B(n)\) | q | \(\alpha(q)\) | \(L\times \alpha(q)\) | Mem(L=262144, q, n) | Decode(L=262144, q, n) |
|---|---|---|---|---|---|---|
| 0 | 21210 | q4_0 | 0.0075 | 1966 | 23176 | 121.2 |
| 3 | 20000 | q8_0 | 0.0125 | 3277 | 23277 | 缺少q8_0,n=3的采样 |
| 6 | 18000 | f16 | 0.021 | 5494 | 23494 | 缺少f16,n=6的采样 |
这三组的实际部署测试结果是:
- 0, q4_0, ncmoe=0, ctx=262144: 120.31t/s (2535 tokens generated), 22726 MiB(空闲: 24576-22726=1850MiB)
这个实际速度结果和理论值121.2t/s非常接近,误差约为0.7%,实际显存占用和理论值23176 MiB也非常接近,误差约为1.9%。
- 3, q8_0, ncmoe=3, ctx=262144: 110.46 t/s (2535 tokens generated), 23446 MiB
- 6, f16, ncmoe=6, ctx=262144: 85.98 t/s (2714 tokens generated), 24120 MiB
后两组的ncmoe=3和ncmoe=6的在实验一中没有测试过,所有没有相关的速度模型常数,无法预测,但显存占用和理论值也非常接近,误差约为0.4%和0.3%。
工程博士的手段,就是这么朴实无华。
而单卡3090部署qwen3.6-35B-A3B+256K上下文的最优方案显然就是直接使用q4_0量化KVCache,不需要卸载到CPU。此时推理速度达到理论物理上界的77%,显存占用方面也有超过1GiB的余量,完全可以满足实际部署的需求。
4.3 近似模型的合理外推测试
速度近似模型一定与显卡强相关,但是显存近似模型大概率只与llama.cpp的版本相关。因此,我们完全可以用3090采样得来的显存模型去预测T4的显存占用情况。从而回答一个进阶问题:
4.3.1 1xTesla T4 + Qwen3.6-35B-A3B + 256K上下文
1张Tesla T4能把Qwen3.6-35B-A3B跑到什么水平?
Tesla T4: 15360MiB 显存,300GB/s,半精度性能65.12TFLOPS
理论上,因为显存限制,只能考虑q4_0和ncmoe>0
65.12TFLOPS的计算性能决定了“A3B”将不会是瓶颈,主要瓶颈是显存。
此时的理论物理上界是 300/6=50T/s。
假设显存的占用模型是设备无关的,只与llama.cpp的实现相关,那么可以使用上面的显存占用公式来推算在Tesla T4上可行的配置。
已知参数
q4_0 的 KV 增长率:α = 0.0075 MiB/token
考虑的上下文长度:
128K:L₁₂₈ = 131072 tokens
256K:L₂₅₆ = 262144 tokens
KV 缓存占用:
KV₁₂₈ = α * L₁₂₈ = 0.0075 * 131072 = 983.04 MiB
KV₂₅₆ = α * L₂₅₆ = 0.0075 * 262144 = 1966.08 MiB
假定可用显存上限:Mem_avail = 14000 MiB
因此,模型权重及固定开销 B(n) 必须满足:
对于 128K:B(n) ≤ 14000 - 983.04 = 13016.96 MiB
对于 256K:B(n) ≤ 14000 - 1966.08 = 12033.92 MiB
B(n) 的线性插值
已知表中数据(部分):
| n | B(n) |
|---|---|
| 16 | 14000 |
| 32 | 6600 |
在区间 [16, 32] 内,我们合理假设B(n) 随 n 线性下降,斜率为:
(6600 - 14000) / (32 - 16) = -7400 / 16 = -462.5 MiB/单位n
因此,在该区间内:
B(n) = 14000 - 462.5 * (n - 16)
从而,我们可以计算满足部署需求的最小 n
128K 上下文
需要 B(n) ≤ 13016.96:
14000 - 462.5 * (n - 16) ≤ 13016.96
462.5 * (n - 16) ≥ 983.04
n - 16 ≥ 983.04 / 462.5 ≈ 2.126
n ≥ 18.126
由于 n 必须是整数,最小 n = 19。
此时:
- B(19) = 14000 - 462.5 * (19 - 16) = 14000 - 1387.5 = 12612.5 MiB
- 总显存 = 12612.5 + 983.04 = 13595.54 MiB,小于 14000 MiB。
256K 上下文
需要 B(n) ≤ 12033.92:
14000 - 462.5 * (n - 16) ≤ 12033.92
462.5 * (n - 16) ≥ 1966.08
n - 16 ≥ 1966.08 / 462.5 ≈ 4.251
n ≥ 20.251
最小整数 n = 21。
此时:
- B(21) = 14000 - 462.5 * (21 - 16) = 14000 - 2312.5 = 11687.5 MiB
- 总显存 = 11687.5 + 1966.08 = 13653.58 MiB,同样小于 14000 MiB。
可行方案汇总
| 上下文长度 | 最小 ncmoe | 预计显存占用 (MiB) | 备注 |
|---|---|---|---|
| 128K | 19 | ~13596 | 非常接近 14GB 上限,余量约 404 MiB |
| 256K | 21 | ~13654 | 余量约 346 MiB,更紧张 |
实际测试结果:
- 128k, q4_0, ncmoe=19 --> 13492 MiB (误差约为0.7%), 42.8t/s with 2700 tokens generated
- 256k, q4_0, ncmoe=21 --> 13670 MiB (误差约为0.4%), 41.6t/s with 2084 tokens generated
256K上下文的41.6t/s达到了理论物理上界的83.2%,而且显存占用也有约1.6GiB的余量,完全可以满足实际部署的需求。
下面是不同ncmoe和上下文长度的实测显存占用和推理速度:
| ncmoe | ctx | 实测显存(Mib) | 实测推理速度(T/s) |
|---|---|---|---|
| 40 | 256k | 4730 | 34 |
| 40 | 128k | 3626 | 34 |
| 40 | 64k | 3148 | 34 |
| 30 | 256k | 9472 | 38 |
| 30 | 128k | 7856 | 38 |
| 30 | 64k | 8368 | 38 |
| 20 | 256k | 14114 | 43 |
显然,3090的显存近似模型可以外推到Tesla T4上,但是,工程暴力破解的局限性也体现出来了,那就是实际上还存在ncmoe=20的更优解,推理速度达到43t/s,显存占用约为13.8GiB,余量约为1.2GiB,也完全可以满足实际部署的需求。
5. 开始想象1张3090部署DeepSeek-V4-Flash + 1M上下文
这次不再先采样然后近似了,一方面是DeepSeek-V4-Flash的参数规模远大于Qwen3.6-35B-A3B,另一方面是llama.cpp对它的支持度还不够完善,最重要的还是,现在我有点信心可以直接进行理论推算了。
DeepSeek-V4-Flash是一个284BA13B的模型,其注意力层使用了CSA + HCA混合注意力,kvcache的效率为最高节省98%。其专家层也包含1个共享专家+256个路由专家,但每次只激活6个专家。
V4 整体架构图包含从 Input Tokens → Embedding → Residual Mixing(mHC)→ CSA/HCA → Residual Mixing → DeepSeekMoE → Prediction Head → MTP Modules 的完整数据流,层间由 Pre-Block Mixing 和 Post-Block Mixing 做 mHC 的通道混合
其中CSA/HCA->DeepSeekMoE 一共重复了43次(Transformer Block * L, L=43)。
那么我们可以同理推算X, Y, Z。
设43层注意力层总权重为X, 43层路由专家的总权重为256Y, 43层共享专家的总权重为Z, 其他层之和暂且视为常数忽略掉(Embedding, Prediction Head和MTP)。
那么有
我们可以先分别估算出Y和X+Z的值,Y=1.084, X+Z=6.496。
因为注意力层和共享专家总是一定在GPU完成计算,所以也不需要具体求解X和Z,后续统一称为XZ
那么推理时一个1token对应需要的固定参数量为X+Z=6.496B, 路由专家总参数量6B=6.504B
然后我们下载usloth量化的Q4_K_XL量化版本,在4bit量化下,按照1B约等于0.5GB的映射关系,那么在存储上:
- 固定开销需要的存储大小为6.496*0.5=3.248GiB
- 路由专家总需要的存储大小为6.504*0.5=3.252GiB
但是我们的3090不支持int4计算,那么在实际计算时需要先将q4还原成fp16, 那么实际计算过程中1个token需要的显存带宽为:
- 13B*2GiB/B = 26 GiB
- (如果只还原成int8)则是13B*1GB/B=13GiB
3090的显存带宽为936GB,但是社区经验告诉我们实际上最多只有300GB能够被有效利用,根据内存墙规律,我们理论上的最大推理速度为:
- fp16: 936/26 = 36 T/s
然而这个理论值完全不可能被实现和验证,因为3090只有24GB显存,我们不可能将DeepSeek-V4-Flash完整放在显存里面。因此必须使用专家卸载技术。
说干就干,开始测算。
在静态显存占用方面:
- 注意力层+共享专家需要的总显存:(X+Z)*0.5=3.248 GB
- 256个专家需要的总显存:Y2560.5=138.752 GB
在动态显存(KV Cache)方面:
我们直接采用最极端的q4_0量化。V4一共有43层重复的注意力层,KV头数为1, 头维度为512, 查询头数为64, 根据deepseek-v4-flash的开源权重config.json,每一层的压缩率分别为:
[
0, 0,
4, 128, 4, 128, 4, 128, 4, 128, 4, 128,
4, 128, 4, 128, 4, 128, 4, 128, 4, 128,
4, 128, 4, 128, 4, 128, 4, 128, 4, 128,
4, 128, 4, 128, 4, 128, 4, 128, 4, 128,
4, 0, 0, 0
]
应该有两层没有压缩,即有21层压缩率是4, 20层压缩率是128, 2层压缩率是1
那么每1KB的kvcache显存占用为:
KV Cache大小 (字节) = 层数 (L) × 2 (K和V) × 隐藏维度 (head_dim) × 键值头数 (num_key_value_heads) × 精度字节数 (FP16为2) × Token数
- 2 * 512 * 1 * (21 / 4 + 20 /128 + 2) * 0.5 * 1024 = 7584 * 0.5 * 1024 = 3,883,008 Byte = 3.703125 MiB
- 那么要达到DeepSeek-V4-Flash的原生1M上下文所需要的显存为 3.703125 * 1024 = 3792 MiB = 3.703125 GiB
假设我们将n层的专家权重放在CPU上,那么就必须满足
其中L是上下文长度(K),R是显存(GiB)
即
接下来是怎么计算推理时需要计算的数据量:
接下来是怎么计算推理时需要计算的数据量:
纯GPU计算时:
- 单token总数据量 =(注意力层+共享专家总参数量)* GPU计算精度 + 路由专家总参数量 * GPU计算精度
- 推理速度 = 显存带宽 / 单token总数据量
采用专家卸载时:
- 单token的GPU数据量: 注意力层+共享专家总参数量)* GPU计算精度 + 路由专家总参数量 * GPU计算精度 * (1-n/43)
- 单token的CPU数据量: 路由专家总参数量 * CPU计算精度 * n/43
- GPU推理速度 = 显存带宽 / 单token的GPU数据量
- CPU推理速度 = CPU内存带宽 / 单token的CPU数据量
- 最终推理速度 = min(GPU推理速度, CPU推理速度)
计算精度/量化压缩级别: fp16(f16)=2, int8(q8_0)=1, int4(q4_0)=0.5
3090支持int8计算,CPU先假设支持int4计算(这一假设来自于我对于CPU高性能计算的无知,以至于后面先进入了幻想时间)计算速度取决于激活量,对于GPU和服务器级别的CPU来说,MoE架构的激活量都太小了,不存在计算瓶颈,影响推理速度的是内存带宽搬运数据的速度,即内存墙。
我通过lmbench的bw_mem测得当锁定一个NUMA, 24线程的时候,CPU read 内存的实际速度是102GB/s, 48 线程则为210GB/s,
所以用这个102-210之间的值来代替内存理论带宽将会更真实,先按102的保守值来计算。3090的显存带宽是960GB/s,但是社区经验告诉我们llama.cpp可能只有20%-30%的利用率,也就是应该用192-288GB来计算(187.5GiB-281.25GiB)
ncmoe还涉及到CPU与GPU之间的通信,而PCIE通信的带宽可以约等于32GB, 仅仅用来传递中间激活值(A13B)的话,就算是fp16精度也完全能cover住,可以认为只有延迟,没有速度损失。
| 上下文长度(K) | min-n | GPU计算精度 | 静态显存(GiB) | kvcache(GiB) | GPU数据量(GiB) | CPU数据量(GiB) | GPU T/s | CPU T/s | 最终 T/s |
|---|---|---|---|---|---|---|---|---|---|
| 1024 | 38 | fp16 | 19.382 | 3.703 | 13.370 | 2.874 | 22.44 | 35.49 | 22.44 |
| 1024 | 38 | int8 | 19.382 | 3.703 | 6.874 | 2.874 | 43.64 | 35.49 | 35.49 |
| 1024 | 38 | int4 | 19.382 | 3.703 | 3.626 | 2.874 | 82.73 | 35.49 | 35.49 |
| 512 | 38 | fp16 | 19.382 | 1.852 | 13.370 | 2.874 | 22.44 | 35.49 | 22.44 |
| 512 | 38 | int8 | 19.382 | 1.852 | 6.874 | 2.874 | 43.64 | 35.49 | 35.49 |
| 512 | 38 | int4 | 19.382 | 1.852 | 3.626 | 2.874 | 82.73 | 35.49 | 35.49 |
| 256 | 37 | fp16 | 22.609 | 0.926 | 13.446 | 2.798 | 22.31 | 36.45 | 22.31 |
| 256 | 37 | int8 | 22.609 | 0.926 | 6.950 | 2.798 | 43.17 | 36.45 | 36.45 |
| 256 | 37 | int4 | 22.609 | 0.926 | 3.702 | 2.798 | 81.04 | 36.45 | 36.45 |
这个估计结果就很符合预期,因为对于MOE模型来说,在启用了专家卸载时,显存和内存构成了双重内存墙,适当的卸载能减轻对CPU内存带宽的需求,但转移给GPU的负载对于显存来说仍然是九牛一毛,从而实现最大化的推理速度。
同理可以考虑双3090的情况,因为没有nvlink,所以必须放弃tensor split, 只能选择layer split, 那么PCIE仍然不会成为瓶颈,此时可以认为显存翻倍,延迟增加,推理速度的推算方式不变,此时:
| 上下文长度(K) | min-n | GPU计算精度 | 静态显存(GiB) | kvcache(GiB) | GPU数据量(GiB) | CPU数据量(GiB) | GPU T/s | CPU T/s | 最终 T/s |
|---|---|---|---|---|---|---|---|---|---|
| 1024 | 31 | fp16 | 41.969 | 3.703 | 13.900 | 2.344 | 21.58 | 43.51 | 21.58 |
| 1024 | 31 | int8 | 41.969 | 3.703 | 7.404 | 2.344 | 40.52 | 43.51 | 40.52 |
| 1024 | 31 | int4 | 41.969 | 3.703 | 4.156 | 2.344 | 72.19 | 43.51 | 43.51 |
| 512 | 30 | fp16 | 45.196 | 1.852 | 13.975 | 2.269 | 21.47 | 44.96 | 21.47 |
| 512 | 30 | int8 | 45.196 | 1.852 | 7.479 | 2.269 | 40.11 | 44.96 | 40.11 |
| 512 | 30 | int4 | 45.196 | 1.852 | 4.231 | 2.269 | 70.90 | 44.96 | 44.96 |
| 256 | 30 | fp16 | 45.196 | 0.926 | 13.975 | 2.269 | 21.47 | 44.96 | 21.47 |
| 256 | 30 | int8 | 45.196 | 0.926 | 7.479 | 2.269 | 40.11 | 44.96 | 40.11 |
| 256 | 30 | int4 | 45.196 | 0.926 | 4.231 | 2.269 | 70.90 | 44.96 | 44.96 |
单卡实际跑通的配置和结果:
| 上下文长度(K) | ncome | GPU显存(MiB) | 推理速度(T/s) |
|---|---|---|---|
| 1024 | 40 | 20918 | 3.6 |
| 512 | 39 | 22958 | 7.5 |
| 256 | 44 | 9290 | 7.55 |
| 256 | 43 | 9290 | 7.90 |
| 256 | 42 | 12554 | 7.97 |
| 256 | 41 | 15816 | 7.65 |
| 256 | 40 | 19086 | 8.3 |
| 256 | 39 | 22346 | 8.4 |
| 128 | 39 | 22044 | 8.4 |
可以得出:
- 纯注意力层+共享专家+llama.cpp固有开销的显存占用是 9290 MiB
- ncmoe从43->42, 显存占用增加3264MiB; 从42->41,显存占用增加3262MiB;基本稳定,直接计算从43->39, 显存占用增加13056MiB,平均每层3264MiB=3.1875GiB,与估算值3.252GiB相差2%。
- 在ncmoe=39的情况下,上下文从128K增加到512K, 显存占用增加了914MiB, 平均每K需要2.38MiB,低于估算的3.703125 MiB,DeekSeek-V4的混合注意力机制比想象中的更厉害
- 减少ncmoe会减少cpu负担,增加gpu负担,但是推理速度却增加了,说明GPU带宽游刃有余,CPU是主要瓶颈
实际推理速度与估算值差距非常大,说明对CPU瓶颈的分析肯定存在问题,潜在的原因:
- CPU内存的随机访问带宽远低于顺序读取,每个Token只激活6个专家,但256个专家分布在CPU内存的不同物理地址。
- CPU没有高效的int4计算能力,或者没有使用,导致实际数据搬运量是int4的2-4倍
这两个原因组合起来倒是能解释实测的7-8T/s
验证这个猜想的方法很简单,再测试一下双卡,将更多的负载转移到GPU就知道了
双卡实际跑通的配置和结果:
| 上下文长度(K) | ncome | sm | ts | GPU0显存(MiB) | GPU1显存(MiB) | 推理速度(T/s) |
|---|---|---|---|---|---|---|
| 1024 | 33 | layer | 5,1 | 22484 | 23200 | 9.48 |
| 512 | 33 | layer | 5,1 | 21360 | 22338 | 9.63 |
| 256 | 33 | layer | 5,1 | 20820 | 22010 | 9.65 |
llama.cpp目前不支持DeepSeek-V4-Flash的tensor split,只能选择layer split
进一步提高了,但是不能超过10T/s。
理论上,ncmoe=33时,GPU负载的路由专家数据量=103.18756/256=0.747GiB,相比于注意力层的数据量不值一提,和单卡时的ncmoe=39没什么区别。因此瓶颈一定在CPU。
为了进一步验证,再试一下3*3090
| 上下文长度(K) | ncome | ts | GPU0显存(MiB) | GPU1显存(MiB) | GPU1显存(MiB) | 推理速度(T/s) |
|---|---|---|---|---|---|---|
| 1024 | 27 | 21,4,5 | 21284 | 22662 | 23200 | 10.72 |
| 512 | 27 | 21,4,5 | 20298 | 21800 | 22344 | 10.63 |
| 512 | 26 | 21,4,5 | 23564 | 21800 | 22338 | 11.48 |
显然,我们每为CPU减少一点负担,CPU的瓶颈就会增大一点。
不妨将CPU的计算精度假设为fp16,那么每层的数据量就是3.1875*4=12.75GiB
- 1024K+卸载40层时,CPU有效带宽是 12.75406/256*3.6 = 43.03125 GiB
- 512K+卸载39层时,CPU有效带宽是 12.75396/256*7.5 = 87.4072265625 GiB
- 256K+卸载39层时,CPU有效带宽是 12.75396/256*8.4 = 97.89609375 GiB
- 1024K+卸载33层时,CPU有效带宽是 12.75336/256*9.48 = 93.485390625 GiB
- 512K+卸载33层时,CPU有效带宽是 12.75336/256*9.63 = 94.9645898438 GiB
- 1024K+卸载27层时,CPU有效带宽是 12.75276/256*10.72 = 86.4928125 GiB
- 512K+卸载26层时,CPU有效带宽是 12.75266/256*11.48 = 89.19421875 GiB
随着卸载层数减少,内存随机地址的影响应该会越来越小,访问速度越来越快,这没问题。基本可以确定,实际上的CPU计算就是先反量化到fp16再交给CPU算的。
如果那么我们的理论预测表格就可以更新为
单卡:
| 上下文长度(K) | min-n | GPU计算精度 | 静态显存(GiB) | kvcache(GiB) | GPU数据量(GiB) | CPU数据量(GiB) | GPU T/s | CPU T/s | 最终 T/s |
|---|---|---|---|---|---|---|---|---|---|
| 1024 | 40 | fp16 | 18.634 | 2.380 | 37.184 | 11.953 | 25.82 | 8.53 | 8.53 |
| 1024 | 40 | int8 | 18.634 | 2.380 | 18.592 | 11.953 | 51.63 | 8.53 | 8.53 |
| 1024 | 40 | int4 | 18.634 | 2.380 | 9.296 | 11.953 | 103.27 | 8.53 | 8.53 |
| 512 | 39 | fp16 | 21.822 | 1.190 | 37.483 | 11.654 | 25.61 | 8.75 | 8.75 |
| 512 | 39 | int8 | 21.822 | 1.190 | 18.742 | 11.654 | 51.22 | 8.75 | 8.75 |
| 512 | 39 | int4 | 21.822 | 1.190 | 9.371 | 11.654 | 102.45 | 8.75 | 8.75 |
| 256 | 39 | fp16 | 21.822 | 0.595 | 37.483 | 11.654 | 25.61 | 8.75 | 8.75 |
| 256 | 39 | int8 | 21.822 | 0.595 | 18.742 | 11.654 | 51.22 | 8.75 | 8.75 |
| 256 | 39 | int4 | 21.822 | 0.595 | 9.371 | 11.654 | 102.45 | 8.75 | 8.75 |
双卡:
| 上下文长度(K) | min-n | GPU计算精度 | 静态显存(GiB) | kvcache(GiB) | GPU数据量(GiB) | CPU数据量(GiB) | GPU T/s | CPU T/s | 最终 T/s |
|---|---|---|---|---|---|---|---|---|---|
| 1024 | 33 | fp16 | 40.947 | 2.380 | 39.276 | 9.861 | 24.44 | 10.34 | 10.34 |
| 1024 | 33 | int8 | 40.947 | 2.380 | 19.638 | 9.861 | 48.88 | 10.34 | 10.34 |
| 1024 | 33 | int4 | 40.947 | 2.380 | 9.819 | 9.861 | 97.77 | 10.34 | 10.34 |
| 512 | 32 | fp16 | 44.135 | 1.190 | 39.575 | 9.562 | 24.26 | 10.67 | 10.67 |
| 512 | 32 | int8 | 44.135 | 1.190 | 19.788 | 9.562 | 48.52 | 10.67 | 10.67 |
| 512 | 32 | int4 | 44.135 | 1.190 | 9.894 | 9.562 | 97.03 | 10.67 | 10.67 |
| 256 | 32 | fp16 | 44.135 | 0.595 | 39.575 | 9.562 | 24.26 | 10.67 | 10.67 |
| 256 | 32 | int8 | 44.135 | 0.595 | 19.788 | 9.562 | 48.52 | 10.67 | 10.67 |
| 256 | 32 | int4 | 44.135 | 0.595 | 9.894 | 9.562 | 97.03 | 10.67 | 10.67 |
这下就基本全对上了。
新的问题在于实际有效带宽在39层达到97GiB之后,再减少卸载层数,有效带宽反而降低了。肯定又有别的因素开始占据主导地位了,但是又对整体影响不是很大。
至于别的因素到底是什么?I don't know, it's time to learn more.
6. 思考环节:MOE架构还能怎么优化?
6.2.1 横向卸载与专家缓存池
llama.cpp的-ncmoe是纵向卸载的,将前n层的专家卸载到CPU上,后续的专家仍然在GPU上计算,整体形成了一个计算流水线,影响首字延迟,但是完全不影响吞吐。
有没有可能,将每一层的x个专家卸载到CPU上,其余的就呆在GPU上。在一次推理时,如果共享专家选择了在GPU上的路由专家,那么就直接在GPU上计算,如果选择了在CPU上的路由专家,那么就将这个专家的权重从CPU搬运到GPU上计算,然后通过缓存池常见的方式踢掉另一个专家的权重去CPU上。这样我们的推理速度瓶颈就更有可能来自于显存带宽,前提是我们的缓存策略设计的足够好,路由专家的轮换频率足够低,否则,频繁的搬运专家权重会导致CPU内存带宽再次成为瓶颈。
6.2.2 注意力压缩
DeepSeek-V4-Flash的混合注意力机制已经证明了注意力压缩的可行性,甚至可以说是非常成功的,建议其他厂商都学习一下。
不过,是否可以从推理引擎的角度,实现一种为任意模型提供注意力压缩的通用方法呢?

浙公网安备 33010602011771号