
最近在本地部署 Ollama 运行大模型时,遇到了一个非常典型的问题:
ollama ps 显示 PROCESSOR: 100% CPU本文记录了从诊断到彻底优化的完整过程,希望能帮到遇到类似问题的朋友。
拿到问题后,首先做了几件事:
# 1. 确认 GPU 驱动正常
nvidia-smi
# 2. 确认 Ollama 版本
ollama --version
# 3. 查看模型实际运行状态
ollama ps关键输出:
NAME ID SIZE PROCESSOR CONTEXT
qwen3.5:9b 6488c96fa5fa 6.1 GB 100% CPU 4096问题确认:6GB 的小模型竟然也 100% 用 CPU 跑,显然不是显存不够的问题,而是 Ollama 根本没有识别到 GPU。
Ollama 提供了 OLLAMA_DEBUG=1 环境变量来输出详细日志。我们先停止所有 Ollama 进程,然后手动启动:
Stop-Process -Name "ollama*" -Force
$env:OLLAMA_DEBUG = "1"
ollama serve这一步让我们发现了关键报错:
level=INFO msg="discovering available GPUs..."
level=DEBUG msg="llama-server discovery: stopped subprocess after collecting GPU info" exit=1
level=DEBUG msg="evaluating which, if any, devices to filter out" initial_count=0
level=INFO msg="inference compute" id=cpu library=cpu name=cpu核心线索:
llama-server在 GPU 发现阶段异常退出(exit=1),检测到的 GPU 数量initial_count=0,最终只能回退到cpu计算后端。
为了排除 Ollama Go 语言外壳的问题,我们直接调用底层的 llama-server.exe:
Set-Location "C:\Users\Admin\AppData\Local\Programs\Ollama\lib\ollama"
.\llama-server.exe --list-devices输出:
Available devices:
(none)实锤了:llama.cpp 层面就看不到任何 GPU 设备。
查看 Ollama 的 lib\ollama\cuda_v12\ 目录:
Name Length
---- ------
concrt140.dll 311,688 字节
cublas64_12.dll 113,720,712 字节
is-8HNSXDBU7C.tmp 692,449,672 字节 ← 可疑的 660MB 临时文件!同时对比主目录下的 ggml*.dll 文件,发现:
应有的文件 | 是否存在 |
|---|---|
ggml-base.dll | ✅ 存在 |
ggml-cpu-*.dll(各种 CPU 架构) | ✅ 都有(十几种) |
ggml-cuda.dll | ❌ 完全缺失! |
读取 is-8HNSXDBU7C.tmp 文件前几个字节,发现是 MZ(PE 可执行文件头),进一步搜索文件内容,发现 7z / PK 等归档签名。
Ollama 安装过程中,CUDA 支持库的自解压包解压失败了! 那个 660MB 的 .tmp 文件应该是包含 ggml-cuda.dll 的自解压程序,但由于安装中断(可能是安装时卡顿、杀软拦截等原因),文件没有被正确解压和重命名,导致所有 CUDA 相关的 ggml 动态库缺失。
没有 ggml-cuda.dll,llama.cpp 的 GPU 后端自然无法初始化,llama-server --list-devices 当然就看不到任何 GPU。
最干净可靠的方式是完全卸载后重装:
OLLAMA_MODELS 指向的模型缓存目录(省的重新下载几十 GB)重装完成后验证:
> llama-server.exe --list-devices
Available devices:
[0] NVIDIA GeForce RTX 3090GPU 识别成功!
重装之后 GPU 是能用了,但新的问题来了:
> ollama ps
NAME ID SIZE PROCESSOR CONTEXT
qwen3:32b 030ee887880f 29 GB 21%/79% CPU/GPU 32768同时 nvidia-smi 显示:
首先要理解一个核心概念:当模型 > 显存时,Ollama 会分层(Layer Offloading):
模型总权重 = GPU 上加载的层 + CPU 上加载的层指标 | 数值 | 含义 |
|---|---|---|
qwen3:32b 模型大小 | 29 GB | 权重总大小 |
RTX 3090 显存 | 24 GB | GPU 能放的上限 |
当前分层 | 21% CPU / 79% GPU | 约 6GB 权重在 CPU,23GB 在 GPU |
为什么 GPU 利用率只有 17%?
因为每一步推理(生成一个 token)都需要:
PCIe 带宽 vs GPU 算力 = 瓶颈,GPU 大部分时间在空转等数据,所以利用率上不去。
💡 经验法则:只要 CPU/GPU 分层比例不是 0%/100%,GPU 利用率通常都很难看。
关键:显存不是只放模型权重的!
显存占用有三大块:
总显存 = 模型权重(Weights) + KV Cache(上下文缓存) + 运行时预留开销组成部分 | 当前占用 | 说明 |
|---|---|---|
模型权重 | ~22.9 GB | 29GB × 79% |
KV Cache | ~1.3 GB | 32K 上下文,fp16 精度 |
预留开销 | ~0.4 GB | 默认预留 + 其他 |
总计 | 24.6 GB | 超出了!所以要卸载权重到 CPU |
省显存 → 把省出来的空间用来多放模型层 → 降低 CPU 分层比例 → 减少 PCIe 搬运 → GPU 利用率提升
那怎么省显存?从三大组成部分逐一开刀:
KV Cache 的大小公式:
KV_Cache_Size ≈ 2 × 层数 × 头维度 × 头数 × 上下文长度 × 2(bytes/fp16)简化后,对 32B 参数模型大致是:
上下文从 32K 降到 4K,直接省出 1GB+ 显存! 这 1GB 可以多塞几层模型进 GPU。
日常对话场景,99% 的情况 4K 上下文完全够用。除非你要一次丢一整篇论文进去让模型总结。
默认 KV Cache 是 fp16(2 bytes/值),可以改成 8bit 量化:
KV Cache 类型 | 每个值占用 | 相对占用 |
|---|---|---|
fp16(默认) | 2 bytes | 100% |
q8_0 | 1 byte + 少量缩放因子 | ~53% |
q4_0 | 0.5 bytes + 缩放 | ~28% |
用 q8_0 可以再省接近一半的 KV Cache,而且精度损失几乎不可感知。
Flash Attention 是一种更高效的 Attention 计算算法,能减少中间结果的显存占用,同时提升计算速度。
Ollama 默认会预留一部分显存(防止 OOM),但我们可以把这个预留调小,榨干每一寸显存。
在 Windows 上,用 PowerShell 永久写入用户级环境变量:
# 1. 上下文长度从 32K 降到 4K ← 最大的优化!
[Environment]::SetEnvironmentVariable("OLLAMA_CONTEXT_LENGTH", "4096", "User")
# 2. KV Cache 8bit 量化
[Environment]::SetEnvironmentVariable("OLLAMA_KV_CACHE_TYPE", "q8_0", "User")
# 3. 启用 Flash Attention
[Environment]::SetEnvironmentVariable("OLLAMA_FLASH_ATTENTION", "true", "User")
# 4. GPU 预留开销设为 0(榨干显存)
[Environment]::SetEnvironmentVariable("OLLAMA_GPU_OVERHEAD", "0", "User")设置完成后,重启电脑(或者至少重启所有 Ollama 进程),环境变量才会生效。
以运行 qwen3:32b 为例:
指标 | 优化前 | 优化后(预期) |
|---|---|---|
上下文长度 | 32768 | 4096 |
GPU/CPU 分层 | 79% / 21% | ~95%+ / ~5%- |
GPU 利用率 | 17% | 60% ~ 95% |
系统内存占用 | 高(~6GB 模型+缓存) | 显著降低 |
推理速度(tok/s) | 慢 | 快 2 ~ 4 倍 |
变量名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| int | 模型默认 | 上下文窗口大小,调小可以省显存换速度 |
| int | 自动计算 | 额外预留的 GPU 显存(bytes),不想预留设 0 |
| bool | false | 启用 Flash Attention(v2+) |
| string | fp16 | KV Cache 精度: |
| int | 1 | 并行处理的请求数,1 时速度最快 |
| string | 全部 | 控制可见的 GPU,如 |
| bool | false | 多 GPU 时是否跨卡分散模型(适合推理,不适合并行) |
如果你的模型 >> 显存,下面是优先级排序:
这次排查花了整整一个下午,但收获很大:
llama-server --list-devices,如果没设备就去查 ggml-cuda.dll 在不在 —— 90% 的情况是安装不完整。ollama ps 里的分层比例和 nvidia-smi 的利用率。希望这篇文章能帮你少踩坑,享受本地跑大模型的乐趣!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。