ollama 有显卡却跑在 CPU 上:先看 ollama ps 的 PROCESSOR 列,再按五层往回查(2026)

Updated on in AI 工具

先说结论: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 自己怎么说的,日志里有两行关键信息

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

容器与 WSL:两套完全不同的排查路径

如果你的 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% CPUdocker 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 那一列。