ollama ps 的 PROCESSOR 列比任何设置项都可信「我明明有显卡,为什么 ollama 还是慢得像在跑 CPU?」这个问题在本地大模型圈子里出现的频率,和「模型下载卡住」差不多高。但两者的性质完全不同:下载卡住是网络问题,跑在 CPU 上是资源调度问题,而且 ollama 在这件事上几乎不给你任何提示——它不会弹窗说「我没用上你的显卡」,它只是安静地退回到 CPU,然后你看着一秒两三个字的速度怀疑人生。
所以这篇文章不列「十大原因」,只给一条能走通的顺序:先定性(到底是不是 CPU 在算),再定位(卡在驱动、后端库、容器、显存还是上下文),最后才谈改设置。 顺序反了,你会花一下午改环境变量,而问题可能只是模型比显存大了 2 GB。
先把最容易搞错的一件事说清楚:慢不等于没上 GPU。 有三种慢长得很像,但成因完全不同。
第一种是加载慢:执行 ollama run 之后要等十几秒甚至几分钟才开始出字。这是把几 GB 的权重从磁盘读进内存/显存,受磁盘速度限制,机械硬盘和网络盘会特别明显。
第二种是首字慢:模型已经加载好了,但你敲完问题要等很久才看到第一个字。这通常是 prompt 处理(prefill)阶段在算,长上下文时特别明显,因为它要把你几千个 token 一次性过一遍。
第三种是出字慢:字出来了,但一秒两三个,像挤牙膏。这才是典型的「跑在 CPU 上」或者「部分层被卸载到 CPU」。
只有第三种,才值得你去查 GPU 有没有被用上。前两种改了 GPU 设置也不会有明显改善,白折腾。
ollama ps 定性,别靠感觉ollama 自带一个几乎没人看的命令,但它恰好是这个问题唯一的一手证据:
$ ollama ps
NAME ID SIZE PROCESSOR UNTIL
qwen2.5:14b-instruct 7cdf5a0187d5 10 GB 100% GPU 4 minutes from now
关键就是 PROCESSOR 这一列。它只有三种形态:
| 显示 | 含义 | 说明 |
|---|---|---|
100% GPU | 全部层在显存里 | 加速正常,慢就是硬件本身的极限 |
100% CPU | 一层都没上显卡 | 显卡没被识别,或显存完全不够 |
48%/52% CPU/GPU 这类分数 | 部分卸载 | 显存装不下全部层,剩下的留在内存里 |
这一列是 ollama 自己算出来的,不是猜的,所以它比 nvidia-smi 里有没有进程更权威。先看它,再决定往哪个方向查。 如果显示 100% GPU 而你仍然觉得慢,那问题不在卸载,而在显卡的显存带宽或者你选的模型规格——继续调环境变量是没有意义的。
还有一个更省事的读法,直接问 HTTP 接口,输出是结构化的,方便脚本判断:
curl -s http://localhost:11434/api/ps
返回的 JSON 里,每个已加载模型都带 size、size_vram 和 details。size_vram 接近 size 就是全在显存,size_vram 为 0 就是纯 CPU。 这个比值比百分比更好判断,因为它不受四舍五入影响。
注意一个前提:ollama ps 只显示当前已加载的模型。如果你刚跑完一条命令就去看,模型可能已经被卸载了(默认空闲 5 分钟回收),列表是空的。所以要先跑一个长回答,让它保持加载状态,再在另一个终端窗口里查。
如果 PROCESSOR 是 100% CPU,第一件事不是改 ollama,而是问「操作系统自己看得见显卡吗」。这一步用一个命令就能定:
# NVIDIA
nvidia-smi
# AMD(Linux 用 rocm-smi,Windows 看任务管理器性能页)
rocm-smi
三种结果,对应三条完全不同的路:
结果一:命令正常输出,能看到显卡型号、显存总量、驱动版本。 说明驱动这一层是通的,问题在 ollama 与驱动之间。往下看第三步。
结果二:nvidia-smi 报「不是内部或外部命令」或者报错退出。 说明驱动没装好,或者装了个不含 nvidia-smi 的残缺版本。这种情况下 ollama 一定看不到 GPU——它调用的是同一套驱动接口。先把驱动装对,重启,再回来。
结果三:nvidia-smi 能跑,但显存占用已经很满。 看一眼进程列表里是谁占的:浏览器开了硬件加速、OBS 在后台、游戏没退干净、另一个推理进程还挂着,都会把显存提前吃掉。这时候 ollama 不是「不想用」,是「用不上」。
有一个特别容易漏的细节:笔记本的双显卡。 很多轻薄本同时有 Intel/AMD 核显和 NVIDIA 独显,ollama 在某些版本里会优先选中核显,然后因为核显显存不足而退回 CPU。判断方法是看日志里 inference compute 那一行写的是哪块卡——如果写的是核显的名字,而你真正想用的是独显,就要显式指定设备。较新版本的 ollama 支持用 OLLAMA_IGPU_ENABLE 控制是否启用核显,具体取值以你安装版本的帮助输出为准(来源:Ollama 官方仓库 discover/runner.go 中对核显过滤逻辑的实现,2026-11 查阅)。
ollama 不会在终端告诉你它选了哪个后端,但服务端日志会。这是整个排查里信息密度最高的一步,也是最容易被跳过的一步。
日志位置:
# Windows
%LOCALAPPDATA%\Ollama\server.log
# macOS
~/.ollama/logs/server.log
# Linux(systemd 安装)
journalctl -u ollama -n 200 --no-pager
打开日志,搜两个关键词:
第一个是 inference compute。 加载模型时 ollama 会打一行,里面带 library= 字段。library=cuda / library=rocm / library=metal 说明用的是 GPU 后端;library=cpu 就是纯 CPU。这一行是定性结论,比任何猜测都准。
第二个是 no compatible GPUs found 或者类似的告警。看到它,说明 ollama 扫描了系统但一台可用的加速设备都没找到,问题在驱动或后端库,不在模型。
如果日志里干净得什么都没有,先打开调试级别再复现一次:
# Windows(PowerShell,临时生效)
$env:OLLAMA_DEBUG="1"
ollama serve
# Linux / macOS
OLLAMA_DEBUG=1 ollama serve
OLLAMA_DEBUG=1 会把设备枚举过程也打出来,包括每块卡的显存、计算能力和被跳过的原因。这一层信息在社区里很难查到,因为大多数提问的人都没开过它。
如果 PROCESSOR 显示的是 xx%/yy% CPU/GPU 这种分数,恭喜,问题已经定位清楚了:模型比你的可用显存大,ollama 把能塞的层塞进显存,剩下的留在内存,每生成一个 token 都要在两条总线上走一趟。 这比纯 CPU 快,但比纯 GPU 慢好几倍。
这里要建立一个容量直觉,用公式而不是背表格,因为表格一换模型就废:
模型权重占用 ≈ 参数量 × 每参比特数 ÷ 8
KV cache ≈ 2 × 层数 × 隐藏维度 × 上下文长度 × 每元素字节数
举个具体例子:一个 14B 参数的模型用 4 bit 量化,权重是 14 × 4 / 8 = 7 GB。看着 8 GB 显存好像够,但还要加上 KV cache、CUDA 上下文、驱动自身占用,实际往往要 9~10 GB。所以「8 GB 卡跑 14B Q4」在真实环境里大概率是分数卸载,不是满速。
要算清自己手上模型的规格,用:
ollama show qwen2.5:14b-instruct
输出里能看到参数量、量化方式、上下文长度上限。拿这三个数套上面的公式,就知道该不该降规格。想更细地对比不同量化档位的显存需求,站内另一篇专门算过这笔账,可以对着看:开源模型微调需要多少显存。
降规格有三个旋钮,效果从强到弱:
换更小的量化。 同一模型从 Q5 降到 Q4,权重直接少两成,通常就够塞进去了。代价是精度略降,日常对话感知不明显。
换更小的模型。 14B 换 7B,权重减半。这是最有效的一招,也是最多人不愿意做的一招。
减少同时加载的模型数。 ollama 默认会在显存里保留多个模型一段时间,两个 7B 同时驻留就能把 8 GB 卡吃满。用 OLLAMA_MAX_LOADED_MODELS=1 限制,或者用完手动 ollama stop。
这三个旋钮的优先级不是我编的,是 ollama 自身的分层策略决定的:它按「层」为单位决定上不上卡,而不是按「模型」为单位整体切换(来源:Ollama 官方 Modelfile 文档中 num_gpu 参数的说明,该参数定义的是送到 GPU 的层数,-1 为自动决定,2026-11 查阅)。所以只要有一层塞不下,你看到的就是分数,而不是干脆的失败。
这一层最隐蔽:很多人模型能全量上 GPU,但聊到一半突然变慢。 原因是 KV cache 随对话变长而增长,超过预留空间后 ollama 会重新调度,把一部分层挪回 CPU。
默认上下文长度在不同版本里改过几次,所以你看到的「一开始很快、聊久了变慢」并不是错觉。(来源:Ollama 官方 FAQ 关于默认 context length 的条目,该值历史上从 2048 上调过,具体数值以你安装版本为准,2026-11 查阅)控制它有两个地方:请求时带 options.num_ctx,或者全局设 OLLAMA_CONTEXT_LENGTH。日常问答 4K~8K 足够,除非你在做长文档摘要,否则没必要开到 32K。
# 临时跑一个短上下文的实例
curl -s http://localhost:11434/api/generate -d '{
"model": "qwen2.5:14b-instruct",
"prompt": "用三句话解释什么是 KV cache",
"options": { "num_ctx": 4096 }
}'
改完再看一次 ollama ps,如果分数变成了 100% GPU,说明就是上下文吃掉了余量。这一招比换模型便宜得多,值得先试。
前面五层都排干净了,才轮到环境变量。因为环境变量是最容易「以为改了其实没改」的一层,很多人在这里绕了几个小时。
坑一:在终端里 export,但服务不是从那个终端启动的。 Windows 上 ollama 通常是托盘程序,开机自启,跟你当前 PowerShell 会话毫无关系。你在 PowerShell 里 $env:OLLAMA_NUM_GPU="99" 只影响这个会话里手动启动的进程,托盘那个照旧。正确做法是在「系统属性 → 环境变量」里设用户级变量,然后从托盘完全退出 ollama 再重新打开。
坑二:CUDA_VISIBLE_DEVICES 被设成了空值。 这个变量的语义是「只让程序看见哪几块卡」,设成空字符串等于「一块都不给」。有些教程为了禁用 GPU 会让你这么设,事后忘了删,就成了隐形的手铐。检查一下:
# Windows PowerShell
Get-ChildItem Env: | Where-Object { $_.Name -like "OLLAMA*" -or $_.Name -like "*CUDA*" -or $_.Name -like "*ROCR*" }
坑三:OLLAMA_NUM_GPU=0 残留。 这个参数控制有多少层送到 GPU,-1 是自动,0 是强制全 CPU。有人为了测试设过 0,之后一直没改回来,于是「显卡明明在,就是不用」。查一下有没有这个变量,有就删掉。
Linux 上还有第四个坑:变量要写进 systemd 单元才生效,直接 export 对已经跑起来的服务无效。
sudo systemctl edit ollama
# 在 [Service] 段下加一行
# Environment="OLLAMA_NUM_GPU=-1"
sudo systemctl daemon-reload && sudo systemctl restart ollama
如果你的 ollama 跑在 Docker 里,前面所有关于「驱动装没装」的判断都要换一套逻辑:容器看不见宿主机的显卡,除非你显式把设备透传进去。
先验证透传是否成立,这一步和 ollama 无关,单独测容器能不能看到卡:
docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi
能打出显卡信息,说明 NVIDIA Container Toolkit 装好了,接着确认启动参数里带了 --gpus all。打不出来,就是 toolkit 没装或者 Docker 的 runtime 没配,先修这个,别在 ollama 的参数上打转。
WSL2 是另一种情况,也是最容易搞错的一种:WSL 里的 GPU 来自 Windows 宿主的驱动,不要在发行版里再装一遍 NVIDIA 驱动。 在 WSL 里装 Linux 版驱动会把 /usr/lib/wsl 那套映射搞坏,症状就是 nvidia-smi 能用但 CUDA 初始化失败。正确顺序是:Windows 装好驱动 → WSL 里 nvidia-smi 能出结果 → 再装 ollama。判断 WSL 里驱动是否正常,看这个目录在不在:
ls /usr/lib/wsl/lib/libcuda.so*
文件在、nvidia-smi 也能跑,才谈得上 GPU 加速。文件不在,说明 Windows 侧的驱动没把 WSL 支持带上。
把上面的顺序压成一张表,出问题时从第一行往下对:
| 现象 | 最先查 | 大概率原因 |
|---|---|---|
ollama ps 显示 100% CPU | 日志里的 library= 字段 | 后端库选成了 cpu |
ollama ps 显示分数卸载 | 显存余量 vs 模型大小 | 模型或上下文超出显存 |
nvidia-smi 命令本身报错 | 驱动安装 | 驱动缺失或不完整 |
nvidia-smi 正常但 ollama 看不到卡 | CUDA_VISIBLE_DEVICES | 变量被设空或被覆盖 |
| 笔记本上核显被选中 | 日志里的设备名 | 双显卡优先级 |
容器里 100% CPU | docker run --gpus all 测试 | 没装 container toolkit |
| WSL 里 CUDA 初始化失败 | /usr/lib/wsl/lib/ | 在发行版里装了驱动 |
| 开始时满速、聊久了变慢 | num_ctx / KV cache | 上下文增长挤爆显存 |
100% GPU 但仍然慢 | 显存带宽与模型规格 | 不是卸载问题,是硬件上限 |
这张表最后一行值得单独说:100% GPU 之后,卸载已经不是瓶颈了,你拿到的速度就是这张卡的物理上限。 这时候再改任何环境变量都是徒劳。判断要不要换卡,看的是显存带宽而不是显存容量——同样是 24 GB,带宽差一倍的两张卡,出字速度能差近一倍。
我的建议分三类人:
只是偶尔跑跑 7B 做翻译、改文案的,不用折腾。核显 + 8 GB 内存跑 Q4 的 7B 是能用的,出字慢一点但结果一样。为这个场景买显卡不划算,不如把模型换成更小的量化版本。
每天要用本地模型写代码、处理长文档的,显存是硬门槛。这时候该看的不是「多大的卡能跑多大的模型」,而是「我的常用上下文长度下,KV cache 要多少」。按上面的公式算一遍再买,比看评测靠谱。
想拿本地模型当生产力主力、替代在线 API 的,说实话,单卡本地部署在 2026 年仍然打不过在线服务,除了隐私和离线两个场景。如果你不属于这两个场景,把钱花在别处更值。
先看 PROCESSOR 定性,再看日志定层,最后才动设置。 中间任何一步跳过去,都会变成在错误的地方找答案。
三个最容易白花时间的地方,替你先排掉:模型比显存大一点点,表现出来和「显卡没被识别」几乎一样;容器和 WSL 的驱动路径和裸机完全不同,裸机经验套不过去;环境变量设了但服务不是从你那个终端启动的,改了等于没改。
顺带提一句,如果你连 ollama ps 都连不上、报 connection refused,那是另一个问题,属于服务没起来或者端口被占,站内这篇从服务层查起:Ollama 连不上 11434 端口怎么排查。而如果你的症状是「拉模型卡在 pulling manifest」,那是下载链路的问题,和 GPU 无关,看这篇:ollama 下载模型太慢、卡在 pulling manifest。
最后补一个社区里反复出现、但官方文档没写的现象(传闻):系统休眠唤醒之后,Linux 上的 nvidia_uvm 模块有时会失效,表现为 nvidia-smi 正常但 ollama 退回 CPU。 遇到这种情况,重新加载模块比重启整机快:
sudo rmmod nvidia_uvm && sudo modprobe nvidia_uvm
这条不是万能药,只在「休眠前好好的、唤醒后变慢」这个特定场景下值得试。其它情况请回到第一步,重新看 PROCESSOR 那一列。