目录
RTX 3080 Laptop(16GB)上用 llama.cpp 跑 Qwen3.6‑35B‑A3B(Q4_K_M,19.7GiB)这种 MoE。权重装不满显存,只能部分层卸到 CPU。下面扫了一圈 -ngl,看速率、显存和大上下文会不会崩,给编码助手定一组能长期用的参数。
MoE 每 token 只激活一小部分专家,decode 比同参数 dense 省算力,但权重还是要全驻留。16GB 塞不下 19.7GiB,混合卸载免不了。
环境
| 项 | 规格 |
|---|---|
| GPU | RTX 3080 Laptop,16GB,约 90W,sm_86 |
| CPU | 11 代 Intel,8 核 16 线程(AVX‑512) |
| 内存 | 64GB ECC |
| 系统 | Ubuntu 22.04 |
| 驱动 | 595.71.05(CUDA 运行时上限 13.2) |
| llama.cpp | CUDA 版,build 571d0d5,Toolkit 12.4 |
| 二进制 | ~/llama.cpp/bin/llama-server / llama-cli / llama-bench |
| 模型 | Qwen3.6-35B-A3B-…-Q4_K_M.gguf,19.70 GiB,34.66B,MoE(约 3B 激活) |
怎么测
速率用官方 llama-bench(pp / tg,带标准差,无 HTTP 干扰)。显存和稳定性用 llama-server 实测。
这版 server 默认不怎么打逐请求 tok/s,大上下文时 HTTP 还可能先回 "Loading model",所以速率不以 curl 为准。
指标:
- pp512:喂 512 token 的 prefill 吞吐
- tg128:生成 128 token 的 decode 吞吐
- VRAM:加载后
nvidia-smi占用 - 稳定性:目标 ctx 下能否真正完成一次生成
顺带记下几个坑:
pkill -f llama-server会把含这段字样的 shell 自己干掉,写成pkill -f '[l]lama-server'。-fa auto不会开 Flash Attention,要-fa on。- 本构建里
--n-gpu-layers auto几乎不往 GPU 卸(只占约 335MiB),必须写死数字。 /health在模型还在加载时就可能 200,就绪以真实生成拿到choices为准。
卸载层数 vs 速率 / 显存
llama-bench,pp512 + tg128,-fa 1 -t 16,重复 2 次取均值:
| ngl | pp512 (tok/s) | tg128 (tok/s) | 状态 |
|---|---|---|---|
| 16 | 470.56 ± 7.49 | 25.11 ± 0.13 | OK |
| 24 | 582.11 ± 5.12 | 32.50 ± 0.71 | OK |
| 28 | 670.28 ± 3.84 | 39.14 ± 0.39 | OK |
| 30 | 723.46 ± 7.93 | 42.88 ± 0.63 | OK(小上下文) |
| 32 | — | — | 加载失败(OOM) |
原始片段:
| model | size | params | backend | ngl | threads | fa | test | t/s | | qwen35moe 35B.A3B Q4_K | 19.70GiB | 34.66 B | CUDA | 16 | 16 | 1 | pp512 | 470.56 ± 7.49 | | qwen35moe 35B.A3B Q4_K | 19.70GiB | 34.66 B | CUDA | 16 | 16 | 1 | tg128 | 25.11 ± 0.13 | | ... | | | | 30 | 16 | 1 | tg128 | 42.88 ± 0.63 | llama_bench: error: failed to load model '...Q4_K_M.gguf' # ngl=32
llama-server 加载,ctx=8192,-fa on:
| ngl | VRAM (MiB) | VRAM (GB) | 余量 (GB) |
|---|---|---|---|
| 0 | 633 | 0.62 | 15.36 |
| 8 | 4319 | 4.22 | 11.76 |
| 16 | 8151 | 7.96 | 8.02 |
| 24 | 11911 | 11.63 | 4.35 |
| 28 | 13863 | 13.54 | 2.44 |
| 30 | 14769 | 14.42 | 1.56 |
| 32 | 15747 | 15.38 | 0.60 |
| 34+ | — | — | OOM |
粗算每层权重大约 0.46 GiB(8151→11911,跨 8 层约 3760MiB)。ngl 16→30,decode 25.1→42.9 tok/s(+71%),prefill 470→723(+54%)。ngl=32 余量只剩 0.6GB,bench 直接加载失败。ngl=30 小上下文最快,但余量只有 1.56GB,大上下文另说。
大上下文(64K)稳不稳
编码会塞文件和历史。ctx=65536,对 ngl=24/28/30 各起一次 server,加载后发约 5441 token 的 prompt 再生成:
| ngl | 进程 | 生成 | VRAM (MiB) | prefill(5441 tok) | 结论 |
|---|---|---|---|---|---|
| 24 | 活 | 成功 | 13185 | 7.65s,约 711 tok/s | 稳,约余 2.7GB |
| 28 | 活 | 成功 | 15271 | 6.57s,约 828 tok/s | 稳,约余 0.7GB |
| 30 | 死 | 失败 | 169 | — | 段错误,core dumped |
ngl=30 在 bench 里能到 42.9 tok/s,一上 64K 就崩。权重已经占约 14.4GB,KV 再分配时显存不够。
ngl=24 下,5441 token 让 VRAM 从 11911 涨到 13185,KV 大约 0.234 MiB/token。KV 主要跟着卸到 GPU 的层走,ngl 越高,大上下文越吃紧。
瓶颈
- 16GB 显存:ngl≥34 加载就 OOM;19.7GiB 权重只能混合卸。
- KV:ngl=30 + 64K 直接崩;上下文一涨,高 ngl 先死。
- 90W 功耗墙:小上下文 decode 上限大约就在 43 tok/s 附近,再抬 ngl 也突破不了多少。
- CPU 卸载带宽:ngl 越低,decode 越慢(25→43)。
ngl auto:本构建基本不可用。
编码向参数
稳态用这一组:
bash~/llama.cpp/bin/llama-server \ -m ~/guff/Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q4_K_M.gguf \ --n-gpu-layers 24 \ --ctx-size 65536 \ -fa on \ -t 16 \ -b 512 \ --host 0.0.0.0 --port 8080 --parallel 1 --metrics
| 指标 | 实测 |
|---|---|
| decode(小上下文) | 32.5 tok/s |
| prefill(约 5.4K) | ~711 tok/s |
| VRAM(5.4K 填充) | 13185 MiB,约余 2.7GB |
| 64K | 能加载并完成生成 |
不选 30 是因为会话上下文会涨,必须给 KV 留空;24 在 5.4K 填充后还剩约 2.7GB。
短上下文、想再快点可以用 ngl=28,ctx 建议降到 32768,decode 约 39.1 tok/s。64K 下只剩约 0.7GB,填满容易崩。
不要用:ngl=30 + 大 ctx;ngl≥32;-ngl auto;-fa auto。
还想硬撑上下文:
- 再降到 ngl=20,大约再腾出 1.8GB,decode 会掉到约 30 以下。
- KV 量化:
-ctk q8_0 -ctv q8_0。更狠的q4_0本机试过,质量尚可但后来没用;要稳一点用q8_0。
decode tok/s (小上下文) 43 ┤ ● ngl=30 (64K 崩) 39 ┤ ● ngl=28 (余 ~0.7GB) 33 ┤ ● ngl=24 (余 ~2.7GB) ← 编码向 25 ┤ ● ngl=16 └──────────────────────────────────── ngl 16 24 28 30 32(OOM)
粗算:ngl 每加 4 层,decode 大约 +7 tok/s,显存大约 +1.8GB。16GB 这张卡上,编码任务我会停在 24。
别的显存怎么套
| 情况 | 建议 |
|---|---|
| ≥24GB | 权重可全上 GPU,ngl=999,不必按这套折中 |
| 12GB | ngl 上限差不多,KV 更紧,可试 ngl=20 + ctx=32768 + KV q8_0 |
| 8GB | ngl≈16,ctx≤16384,或换更小模型 |
| CPU 无 AVX‑512 | 卸到 CPU 的层更慢,会更倾向抬高 ngl |
| 不确定上限 | 先 llama-bench -ngl 16 -ngl 24 -ngl 28 -ngl 30 -ngl 32 …,最后一个能加载的当小上下文上限,再减 4 层做大上下文 |
顺序:bench 找能加载的最大 ngl → 大上下文用 N_max−4 左右 → 显式 -fa on 和 ngl → 再用真实大 prompt 验一次。
故障
| 现象 | 处理 |
|---|---|
failed to load model / OOM | 降 ngl,每次 −4 |
Segmentation fault | 降 ngl / 降 ctx,或开 KV q8_0 |
| 速率异常低 | 确认 -fa on、ngl 写对了,看 nvidia-smi |
| auto 只占几百 MiB | 改成显式 ngl |
| 一直 "Loading model" | 等真正生成返回 choices |
pkill 把自己干掉 | pkill -f '[l]lama-server' |
复现
速率:
bash~/llama.cpp/bin/llama-bench \ -m ~/guff/Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-Q4_K_M.gguf \ -ngl 16 -ngl 24 -ngl 28 -ngl 30 -ngl 32 \ -p 512 -n 128 -fa 1 -t 16 -r 2
显存:
bashfor N in 0 8 16 24 28 30 32; do
pkill -9 -f '[l]lama-server' 2>/dev/null; sleep 2
~/llama.cpp/bin/llama-server -m <MODEL> --n-gpu-layers $N --ctx-size 8192 \
-fa on -t 16 --host 127.0.0.1 --port 8190 > /tmp/v.log 2>&1 &
sleep 18
echo "ngl=$N vram=$(nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits)MiB"
pkill -9 -f '[l]lama-server' 2>/dev/null
done
64K 稳定性:
bashfor NGL in 24 28 30; do
pkill -9 -f '[l]lama-server' 2>/dev/null; sleep 3
~/llama.cpp/bin/llama-server -m <MODEL> --n-gpu-layers $NGL --ctx-size 65536 \
-fa on -t 16 --host 127.0.0.1 --port 8196 > /tmp/stab.log 2>&1 &
pid=$!; sleep 50
vram=$(nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits)
alive=$(ps -p $pid >/dev/null && echo 1 || echo 0)
curl -s http://127.0.0.1:8196/v1/completions -H 'Content-Type: application/json' \
-d '{"prompt":"<5441-token prompt>","max_tokens":32,"temperature":0}' \
| grep -q choices && gen=1 || gen=0
echo "ngl=$NGL alive=$alive gen=$gen vram=${vram}MiB"
kill $pid 2>/dev/null; pkill -9 -f '[l]lama-server' 2>/dev/null
done
小结
编码向:--n-gpu-layers 24 --ctx-size 65536 -fa on -t 16。小上下文 decode 约 32.5 tok/s,5.4K prefill 约 711 tok/s,还能撑 64K。要速度、上下文不长可以试 28;30 以上配大 ctx 别碰。
bench 上最快的 ngl,不一定能扛大上下文。最终参数要用真实长 prompt 验过再定。
ai协作,人工编辑