llama.cpp 分层 Offload:低显存显卡也能跑大模型

llama.cpp 是 GGUF 推理的元老,它的分层 Offload 能把模型按层拆开,一部分放显存、一部分放内存,让 8GB 甚至 6GB 的显卡也能带动 14B 模型。这篇文章从原理讲到实操参数,附实测数据。

llama.cpp 终端

一、为什么要分层 Offload

先回顾一下显存换算公式:14B 模型用 Q4_K_M 大约要 10GB 显存。如果你的显卡只有 8GB,直接整模型扔进显存装不下,程序直接报 OOM。

传统思路是「全 CPU 跑」:不占显存,但纯 CPU 推理慢得让人怀疑人生——7B 模型在主流 CPU 上大概只有 3~6 token/s,做个翻译都要等半天。

llama.cpp 给了第三条路:分层(Layer Offload)。大模型的 Transformer 结构是一层层堆叠的,每一层都是「Attention + FFN」。既然装不下全部,就装一部分:把前 N 层放 GPU,剩下的层放 CPU,推理时每层算完把结果传给下一层,GPU 和 CPU 协同工作。

这样做的核心收益:显存不够时,用一部分 CPU 计算换「模型整体能跑」。速度比全 GPU 慢,但比全 CPU 快得多,是低显存显卡的唯一解。

要理解分层为什么可行,得先了解推理过程。大模型生成文字时,每一步都要把整个模型「过一遍」:从第一层到最后一层,逐层计算。整个过程的计算量由模型总层数决定,所以理论上每一层都缺一不可。但层的计算是「串行」的——第 3 层必须等第 2 层算完才能开始。串行意味着「谁先算、算完放哪」可以由你分配:把前 20 层放 GPU 加速算,后 20 层放 CPU 慢慢算,结果一样,只是速度取决于较慢的那一部分。这就是分层 offload 的原理。

分层拆解

二、核心参数:-ngl 一行搞定

llama.cpp 最核心的显存参数是 --ngl(n_gpu_layers,GPU 层数):

1
./llama-cli -m qwen3-14b-q4_k_m.gguf \ -ngl 20 \ -c 4096

-ngl 20 表示前 20 层放 GPU,其余放 CPU。注意:

  • -ngl 999 或 -ngl -1 表示全部层放 GPU(有多少层放多少层);

  • -ngl 0 表示纯 CPU 推理;

  • 剩下的层会自动走 CPU(llama.cpp 会自动调度,无需额外参数);

  • KV Cache 也会按比例分配,上下文越大越吃显存,-c 4096 时留意总占用。

那怎么知道 14B 模型到底有多少层、该放多少层进 8GB 显存?用 --verbose 查看加载日志,或者直接看模型信息:

1
./llama-cli -m qwen3-14b-q4_k_m.gguf --verbose -ngl 0 2>&1 | grep -i "n_layer"

也可以先在纯 CPU 模式下跑一次,日志会打印 llama_model_load: n_layer = 40 这样的信息。拿到总层数后,用下面的经验方法估算。

三、层数怎么定:公式与试错

llama.cpp 加载日志会告诉你每一层占多少显存(单位 MiB),格式类似:

1
llm_load_tensors: offloading 20 repeating layers to GPUllm_load_tensors: VRAM used: 5120.00 MiB

实际操作中不需要精确计算,按这套试错流程走:

  • 看剩余可用显存:nvidia-smi 看空闲显存,比如 8GB 显卡在桌面系统下实际可用约 6.5GB;

  • 估单层大小:14B Q4_K_M 共 40 层、总权重约 8.2GB,平均单层约 0.2GB;

  • 初值:可用显存 6.5GB,先给 -ngl 28(留一点给 KV Cache 和 CUDA 上下文);

  • 跑一次看报错:如果 CUDA error: out of memory,就减 2~4 层再试;

  • 看日志:加载完日志末尾会打印 offloaded 28/40 layers to GPU,GPU 显存占用一目了然。

提示:-ngl 减到显存刚好装下后,如果想进一步压榨,可以同时把 -c(上下文)调小,因为 KV Cache 会占 1~3GB。先调层数再调上下文,效果更可控。

四、其他影响显存的参数

除了 -ngl,还有几个参数直接影响低显存场景的成败:

参数 作用 低显存建议
-c / --ctx-size 上下文长度,KV Cache 大小正比 1024~4096,越短越省
--no-mmap 关闭内存映射,全部加载进内存 默认 mmap 即可,不手动关
--gpu-layers-split 多 GPU 时手动分配各卡层数 单卡不用
--flash-attn 开启 Flash Attention,省显存 建议开启
-t / --threads CPU 线程数 物理核数以内
--split-mode none 禁用多卡分片 单卡场景保持默认

一个实用组合示例(8GB 显卡跑 14B):

1
./llama-cli -m qwen3-14b-q4_k_m.gguf \ -ngl 26 \ -c 2048 \ --flash-attn \ -t 8

五、实测数据:8GB 显卡跑 14B

用 8GB 显卡(RTX 4060)实测 Qwen3 14B Q4_K_M(40 层),记录不同 -ngl 的表现:

-ngl GPU 显存占用 输出速度 体验评价
0(纯 CPU) 0GB 2.8 token/s 慢到不可用
10 2.1GB 6.5 token/s 勉强能聊天
20 4.2GB 11.2 token/s 可接受
26 5.4GB 13.8 token/s 流畅
30 6.3GB 15.1 token/s 更快
40(全 GPU) 约 9GB 24.5 token/s 但会 OOM

数据说明几个规律:

  • 每多 offload 约 10 层,速度涨约 40%,所以「能放多少放多少」是铁律;

  • 纯 CPU 和全 GPU 速度差近 9 倍,分层 offload 处在中间偏上;

  • 8GB 显卡跑 14B 的甜点是 -ngl 26~28:显存刚好、速度可接受,再往上就 OOM。

提示:上表的绝对数值受 CPU 内存带宽影响很大。内存是双通道还是四通道、DDR4 还是 DDR5,CPU 侧速度能差两三倍。AMD 平台(内存带宽高)分层 offload 的体验普遍好于 Intel。

六、低显存显卡的完整配置建议

不同显存档位给出可直接抄的配置:

显卡 模型 -ngl 上下文 预期速度
6GB(GTX 1660/3060 Laptop) Qwen3-8B Q4 20 2048 约 18 token/s
8GB(RTX 4060/3070) Qwen3-14B Q4 26 2048 约 14 token/s
8GB(RTX 4060/3070) DeepSeek-R1-Distill-14B Q4 24 2048 约 12 token/s
12GB(RTX 3060/4070) Qwen3-32B Q4 32 2048 约 10 token/s
16GB(RTX 4080/4070TiS) 32B Q4 全量 全部 4096 约 18 token/s

为什么我建议「显存优先给模型,不给上下文」

低显存场景下,一个反直觉但很实用的决策:宁可把上下文缩到 1024~2048,也要把层数放满。原因是速度体验优先——上下文短一点,多轮对话时偶尔丢点记忆,但整体响应流畅;反过来如果层数不够、大半模型在 CPU 上爬,每次回答都要等十几秒,再长的上下文也用不上。层数满了之后,如果显存还有富余,再一步步加上下文。

量化与分层的组合策略

分层 offload 和量化是「叠加关系」而不是「二选一」。两者配合能让显存利用率更高:

策略 显存占用 效果
14B Q4 分层 约 5.4GB 速度约 14 token/s
14B Q3 分层 约 4.5GB 可多放几层,速度约 15~16 token/s
14B Q5 分层 约 6.5GB 层数少放些,质量更高,速度略降

规律:量化降低单层体积 → 同样显存能放更多层 → 更多层跑在 GPU → 速度反而可能提升。所以「降量化 = 纯牺牲质量」也不对,显存紧张时降一档量化换来更多 GPU 层,整体体验可能更好。建议在 Q3~Q5 之间试两轮,找出自己任务上「质量 vs 速度」的平衡点。

七、进阶:K 采样与长上下文的显存陷阱

低显存场景还藏着一个常被忽略的显存陷阱:长上下文的预填充(prefill)峰值。模型读入一大段提示词时,会把整段 prompt 一次性计算,KV Cache 瞬时膨胀——这段峰值可能比生成阶段高 30~50%。所以如果你要给模型喂一大段文档做总结,先评估 文档长度 + 回答长度 的总 token 会不会把显存顶爆,必要时先给 -c 留足余量,或者拆段喂入。

实测经验:-c 2048 在 8GB 显卡上,如果 prompt 本身就有 1500 token,预填充瞬间显存能飙到接近上限。对策是给 -ngl 再留 1GB 缓冲,或者干脆在长文档场景把 -c 提到 4096、同时把 -ngl 降几层——因为预填充阶段 GPU 参与越少,峰值越低,代价是生成慢一点。具体怎么权衡,用下面这个检查清单快速判断:

  • nvidia-smi 确认空闲显存;

  • 估算「模型层数所需 + 上下文 KV + 1GB 缓冲」;

  • prompt 特别长时,把 -c 按「提示词 + 回答」总量设置;

  • 跑一次观察日志里的 VRAM used,与步骤 2 估算对比;

  • 峰值阶段出现 OOM,优先降 -c,其次降 -ngl。

八、几个常见问题

Q:为什么显存明明够了还 OOM?

KV Cache 没算进去。-c 4096 的 KV Cache 在 14B 上约 1.53GB,加上模型权重和 CUDA 上下文,超出显存就会 OOM。对策:先减 -c,或减 24 层。

Q:分层 offload 和全 GPU 的回答质量一样吗?

一样。分层只是把不同层的计算放到不同设备,模型权重、推理逻辑完全不变,答案质量只取决于量化档位,与 offload 方式无关。

Q:Ollama 也能分层吗?

Ollama 用的是 llama.cpp 内核,会自动判断并分层。想精细控制层数,Ollama 可设 num_gpu(对应 -ngl):

1
OLLAMA_NUM_GPU=26 ollama run qwen3:14b

但 Ollama 封装的自由度不如直接用 llama.cpp,追求极致的性能或显存利用,建议回到 llama.cpp。

Q:分层 offload 适合哪些人,不适合哪些人?

适合:显存差一口气(比如 8GB 想跑 14B)、想榨干现有硬件的玩家。不适合:对延迟极其敏感的场景(服务化并发)、有预算直接上大显存卡的人——分层 offload 本质是「妥协方案」,有条件还是直接上满显存体验最好。

Q:多张显卡怎么用分层?

多卡场景用 --gpu-layers-split 手动分配,例如 --gpu-layers-split 20,10 表示第一张卡 20 层、第二张卡 10 层。默认 --split-mode layer 会按比例自动分配,但显存不均衡时手动分配更可控。注意:分层 offload 的原理(层串行)在多卡间同样适用,两张卡一起算的收益主要来自把更多层放进 GPU,而不是「并行加速同一层」。

Q:如何判断我的 CPU 适合分层 offload?

CPU 侧的关键指标是内存带宽(不是核数)。AMD 平台内存带宽高,CPU 层跑得快,分层 offload 体验更好;Intel 平台一般弱一些。内存是双通道还是四通道影响也很大。判断方法很直接:先跑一次纯 CPU(-ngl 0),如果纯 CPU 速度已经可以接受(比如 5+ token/s),那分层 offload 的效果会不错;如果纯 CPU 只有 1~2 token/s,分层的上限也高不到哪去。

Q:分层 offload 会损坏模型文件吗?

完全不会。offload 只是「加载方式」不同,模型文件是只读的 GGUF,加载多少次都不会变。担心的朋友可以放心反复实验参数。

九、调试参数时的踩坑记录

最后分享几个实际调试中遇到过的坑,帮大家少走弯路:

  • -ngl 设置成负数导致纯 CPU:-ngl -1 是「全部放 GPU」,但有些版本把 -ngl -2 之类解释成「自动判断」之外的错误值,反而退化为纯 CPU。保险起见,全 GPU 就用 -ngl 999。

  • –flash-attn 在某些显卡上不生效:老显卡(如 GTX 16 系)对 Flash Attention 支持不完整,开启后可能报错或速度不升反降。遇到这种情况去掉这个参数即可,不影响正确性。

  • Windows 上的显存显示虚高:任务管理器显示的「共享 GPU 内存」会把系统内存也算进去,看着有 16GB,实际专用显存只有 8GB。规划时以 nvidia-smi 的专用显存为准。

  • 改了参数没生效:llama.cpp 的命令行参数必须在启动时传入,运行中改无效。习惯上把常用命令存成一个 shell 脚本(.sh / .bat),换参数只改脚本,避免手滑。

  • OOM 报错时机晚:有时候不是加载时 OOM,而是跑了一会儿(预填充阶段)才崩。所以「加载成功 ≠ 一定能跑完」,长 prompt 场景要按上文「预填充峰值」的思路预留缓冲。

小结

llama.cpp 的分层 Offload 是低显存显卡跑大模型的「保命技能」:-ngl 决定多少层放 GPU,配合 -c 控制 KV Cache,就能用 8GB 显卡带动 14B 模型,速度比纯 CPU 快 4~5 倍。核心口诀:层数放满显存能装下的极限,上下文能短则短,Flash Attention 记得开。配合「量化 + 分层」的叠加策略,显存紧张时也能找到质量与速度的平衡点。如果你正在用 8GB 显卡纠结「要不要换卡」,不妨先按本文把参数调一遍——大多数 14B 场景,分层 offload 已经足够日常使用,省下的升级预算可以花在别处。

延伸阅读: