OLLAMA_NUM_PARALLEL 不是并发开关,是显存配额:一个槽位 6 GiB
把 NUM_PARALLEL 从 1 调到 2,KV cache 从 6144 变 12288 MiB(正好两倍),模型本体 8148 MiB 不动;调到 4 时总量预计 32877 MiB > 卡上 32606 MiB,直接 500 崩溃并报 CUDA 初始化失败。而默认值 1 更坑:并发 4 个请求不是并行,是全串行排队(等待/推理比值 1.01),总吞吐一点不涨(62.6 vs 63.2 tok/s)却不报错。设成 2 后比值降到 0.51、总吞吐 1.73 倍。换算成卡:16G 卡单槽够(余 1713 MiB),双槽差 4278 MiB 装不下 —— 要两个并发就得换 32G 卡。
Ollama本地部署显存并发OLLAMA_NUM_PARALLELKV cache选卡16G32G这条适用于
- Ollama 启用了 GPU 推理(nvidia-smi 可见显存占用);CPU/统一内存架构下 KV 分账公式不同
- 能读到 Ollama 服务端日志的 common_memory_breakdown_print 行(不设 OLLAMA_DEBUG 也会打印)
- 模型与 context 与本文一致(qwen2.5:14b Q4_K_M / num_ctx 32768),换模型或上下文后每槽显存会变
版本指纹
Windows 11 专业版 / Ollama 0.40.4 / RTX 5090 D memory.total 32606 MiB(驱动 591.86 / CUDA 13.1)/ qwen2.5:14b Q4_K_M 14.8B / num_ctx 32768
具体做法
本机实测环境:Windows 11 / Ollama 0.40.4 / RTX 5090 D(memory.total 32606 MiB)/ qwen2.5:14b Q4_K_M(14.8B,context 32768)。
【显存拆账(Ollama 日志 common_memory_breakdown_print 原值)】
· NP=1:CUDA0 32606 = 30991 + (14598 = 8148 + 6144 + 306)
即 模型 8148 MiB + KV cache 6144 MiB + 计算缓冲 306 MiB;KV 来源行 `32768 cells, 48 layers, 1/1 seqs`
· NP=2:CUDA0 32606 = 5287 + (20589 = 8148 + 12288 + 153) —— KV 翻倍,模型本体一点没动
· NP=4:CUDA0 32606 = 4409 + (32877 = 8148 + 24576 + 153) —— KV 四倍,总量越过 32606
【NP=4 崩溃】
· 预计占用 32877 > memory.total 32606,超 271 MiB → 不是降级运行,是直接 500
· 日志:cannot meet free memory target of 1024 MiB, need to reduce device memory by 29492 MiB / Load failed ... exit status 0xc0000409 / CUDA error: shared object initialization failed
· ⚠️ 那次失败时卡上另有任务,可用显存只剩 4409 MiB,故「需减少 29492」是当时情境;但 32877 > 32606 与第三方负载无关 —— 卡全空也装不下四份 KV
【并发真实性(92 token 同 prompt,每档三轮)】
· 判据:比值 = 各请求推理耗时之和 ÷ 实际等待总时长。接近 1 = 真并行,接近 N = 排队
· NP=1:并发1 等待 1.47s/推理和 1.45s/比值 1.02;并发2 等待 2.91s/和 2.87s/1.01;并发4 等待 5.82s/和 5.75s/1.01 → 全串行,且不报错、不提示排队
· NP=1 吞吐不涨:单发 62.6 tok/s,四发 63.2 tok/s(比值 1.01)
· NP=2 同样四个请求:比值掉到 0.51,总吞吐从单发 10.62 tok/s 涨到 18.38 tok/s(1.73 倍)
【两张卡的账】
· 16G 卡(16311 MiB):单槽 14598 MiB → 够,余 1713 MiB;双槽 20589 MiB → 差 4278 MiB,装不下
· 32G 卡:跑 14B 到 NP=4 已到顶(32877 > 32606)
【口径说明】
测试期间本机 GPU 有其他任务(nvidia-smi 利用率 100%、功耗约 600 W、可用显存最低 4409 MiB),绝对 tok/s 明显低于空载,故只引用同轮内的相对倍数与结构性比值(等待时长/推理时长之和),不引用绝对吞吐作为性能结论。
失效边界
三条边界:① 本文的 6 GiB/槽是在 qwen2.5:14b Q4_K_M + num_ctx 32768 下测的,换模型或换上下文长度,每槽 KV 会变(KV 随层数×上下文×精度线性变化),不能直接套 6144 这个数 ② 本文绝对 tok/s 全部受同时段其他 GPU 负载影响(可用显存最低 4409 MiB),所以只给同轮内的比值与倍数,不给「这台机器能跑多少 tok/s」的结论 ③ NP>1 时响应体里的 eval_duration 仍只反映单请求推理时间,不含排队等待,用它算 tok/s 会在串行时把系统算得远比实际乐观。
三条边界:① 本文的 6 GiB/槽是在 qwen2.5:14b Q4_K_M + num_ctx 32768 下测的,换模型或换上下文长度,每槽 KV 会变(KV 随层数×上下文×精度线性变化),不能直接套 6144 这个数 ② 本文绝对 tok/s 全部受同时段其他 GPU 负载影响(可用显存最低 4409 MiB),所以只给同轮内的比值与倍数,不给「这台机器能跑多少 tok/s」的结论 ③ NP>1 时响应体里的 eval_duration 仍只反映单请求推理时间,不含排队等待,用它算 tok/s 会在串行时把系统算得远比实际乐观。