Ollama多GPU深度解析:张量并行、调度机制与性能优化实践
先聊一个我自己踩过的坑。某个周六晚上我把一台双卡机器搬到工位上兴致勃勃装了Ollama拉下来一个70B的量化模型心想两张4090怎么着也比单卡快。结果跑起来一看两张卡确实都占了显存但利用率一个天上一个地下第二张卡时不时就在摸鱼。更让我没想到的是后来我随便跑一个7B小模型第二张卡直接原地罢工连显存都不碰。那晚我几乎把网上关于Ollama多GPU的帖子翻了个底朝天最后发现真相很简单Ollama对多张GPU的调度逻辑和大多数人直觉里的“多卡自动加速”根本不是一回事。这篇文章我就把多GPU跑Ollama这件事讲透包括它底层到底怎么切分模型、哪些环境变量真正起作用、三种多卡玩法分别适用什么场景、性能瓶颈出在哪里以及一张完整的排查链路。无论你是想把70B这种单卡装不下的模型拆到两张卡上还是想在一台多卡机器上同时服务多个模型、多个用户都能在里面找到直接能用的配置。1. 先搞清楚Ollama的多GPU调度机制一张卡不够时它到底做了什么1.1 “按层拆”的张量并行才是Ollama多卡的本质Ollama本身不实现训练框架它做推理的后端核心是llama.cpp那套引擎。在这个引擎里多GPU运行一个模型靠的是张量并行Tensor ParallelismTP。大模型的GGUF权重文件里本质上是几十层Transformer block每层内部又分attention、MLP这些子模块。张量并行的思路就是把模型按层切开——比如一个70B模型有80层两张卡就各分40层。推理的时候前向计算每过完一层就要把中间激活值广播到另一张卡上两边算完再汇总才能进入下一层。这个机制决定了多卡性能的上限完全依赖于卡间通信质量。你可以把每张GPU想象成一个车间车间内部流水线跑得飞快但两个车间之间只有一条窄传送带每加工完一步都要把半成品搬过去再搬回来效率自然会被拖累。所以同一个模型在NVLink互联的卡上跑和走PCIe普通通道跑体验完全是两个级别。1.2 “模型跨卡”和“模型各占一卡”是两件不同的事很多刚上手的人说“多GPU跑Ollama”其实可能同时表达两种需求而且经常混在一起一个模型太大单卡显存放不下需要拆到多张卡上一起跑这叫张量并行一台机器同时部署了好几个模型希望它们分散到不同卡上不要全挤在一张卡里。这两个诉求的实现路径完全不同。模型跨卡由Ollama的GPU加载逻辑控制关键环境变量是OLLAMA_NUM_GPU而模型在卡之间的分配则依赖调度器根据每张卡的剩余显存、OLLAMA_MAX_LOADED_MODELS和OLLAMA_KEEP_ALIVE综合决定。还有一个高频误解以为一张卡装一个模型两个并发请求就会分别派给两张卡实现一人一张卡的处理。真实情况是Ollama对同一个模型的并发请求默认是在同一个模型实例里通过上下文切换轮流处理的不会自动变成“两个GPU副本各处理一条请求”。要真正做到按卡分流、提升吞吐反而要手动开多实例——这一点我放到第3章的方案三里详细讲。概念含义配置入口张量并行TP单个模型按层拆分到多张GPUOLLAMA_NUM_GPU、CUDA_VISIBLE_DEVICES模型共存调度多个模型根据显存分配到不同GPUOLLAMA_MAX_LOADED_MODELS、OLLAMA_KEEP_ALIVE数据并行多实例请求分摊到不同GPU上的多个实例多个ollama serve进程、端口隔离这张表建议直接存下来后面所有配置都逃不出这三个方向。2. 开工前的环境检查驱动、容器与GPU可见性一个都不能少Ollama不帮你装驱动它只是通过CUDA或ROCm运行时去调用GPU。所以很多“多卡不生效”的问题最终查下来根因根本不在Ollama而在环境层。这一章我不会面面俱到只写我实际遇过、也最容易被忽略的几个点。2.1 NVIDIA平台驱动和容器是重灾区先说宿主机。NVIDIA的GPU宿主机只需要装好驱动Ollama的官方镜像内部已经带了CUDA runtime不需要你另外在容器里装CUDA。驱动是否正常一条命令就能确认nvidia-smi如果这里能列出所有GPU说明驱动层面没问题。接着看容器。很多人习惯用docker run --gpus all启动Ollama但如果宿主机没装NVIDIA Container Toolkit这条命令会直接报错或者静默忽略GPU。更隐蔽的是用docker-compose的时候deploy.resources.reservations.devices配置里GPU数量写错容器里就只能看到一张卡。每次启动容器后建议立刻进容器确认一下docker run --rm --gpus all ollama/ollama nvidia-smi输出里能看到几张卡再往下走。如果这里就只剩一张卡后面所有Ollama配置都白搭问题先回到容器层排查。2.2 AMD平台ROCm的支持边界如果你是A卡用户Ollama同样支持但需要拉ollama/ollama:rocm这个标签的镜像。AMD这边多卡问题更多驱动版本、ROCm版本和显卡代际的匹配关系比较敏感rog smi先确认系统识别了几张卡是第一步。这里提醒一点WSL2里的GPU透传行为和原生Linux不完全一样你在WSL2里能看到的GPU不一定代表原生Linux环境会有同样的表现。如果要在生产环境长期跑建议优先考虑原生Linux而不是WSL2省得后面排查环境差异耗费大量时间。2.3 确认Ollama真的看到了所有GPU环境变量OLLAMA_DEBUG1是非常有用的诊断开关。启动服务时这样跑OLLAMA_DEBUG1 ollama serve正常识别多卡时启动日志里会多次出现类似inference compute id0、inference compute id1的记录。如果你只看到一条说明Ollama只识别了一张卡这时候哪怕你把它吹上天它也不会用第二张。日常运行中可以用ollama ps看当前模型加载到了哪张卡上输出里的PROCESSOR列会直接标明是CPU还是GPU。如果再配合nvidia-smi dmon或nvtop实时看显存和利用率环境层基本就能做到滴水不漏。3. 多GPU的三种玩法拆大模型、卡上分家、进程分流这一章是实操核心。三种玩法解决三类问题我按场景拆分来讲。3.1 方案一单模型自动跨卡让一张卡装不下的模型跑起来适用场景70B、甚至更大参数的量化模型单卡显存不够要拆到多张卡。操作步骤用第2章的方法确认所有卡都可见拉一个单卡装不下的模型比如llama3.3:70b-q4_K_M启动服务前设置关键环境变量export OLLAMA_NUM_GPU0,1 ollama serve这里有个容易踩的版本坑Ollama较新版本0.5.x之后中OLLAMA_NUM_GPU的值是GPU序号列表比如0,1表示用第0和第1张卡而在旧版本里这个变量可能表示“把多少层放到GPU”比如OLLAMA_NUM_GPU40意味着只把40层丢给GPU。如果你照着网上的老帖子设置了数字新版本可能根本不会按你的预期走。最稳妥的做法是启动后立刻ollama ps确认。模型加载后ollama ps里的PROCESSOR列如果显示GPU并且跨两张卡说明TP生效了。这套配置下Ollama会尽量把模型权重和KV Cache都塞进显存塞不下才溢出到CPU。如果你的卡之间存在NVLink体验会非常好如果只是PCIe互联性能可能比“单卡部分CPU offload”快不了多少这一点我在第4章会专门展开。3.2 方案二多模型各占一张卡让调度器按显存分家适用场景一台多卡机器上同时跑好几个模型希望互不干扰。实际操作上不需要强制指定谁去哪张卡Ollama的调度器会根据每个模型的显存需求和每张卡的剩余空间自动决定。你只需要给足调度器空间export OLLAMA_MAX_LOADED_MODELS2 export OLLAMA_KEEP_ALIVE30m ollama serveOLLAMA_MAX_LOADED_MODELS决定了同时最多驻留几个模型超过这个数会按LRU策略卸载最久没用的模型。OLLAMA_KEEP_ALIVE控制模型在显存里的保留时间如果设为-1就是永久驻留适合模型切换不频繁的场景。跑起来之后用ollama ps观察正常情况下不同模型会落在不同的GPU上。但要注意一个前提这些模型本身都要小于单卡显存。如果其中一个模型大到单卡放不下调度器会优先采用张量并行把它拆到多卡而不是强行“各占一卡”。3.3 方案三多实例数据并行多用户并发的最优解适用场景显存足够但要同时服务大量请求单实例排队比较严重。原理不复杂一个ollama serve进程处理一份模型再多的并发请求都会在这个进程里排队。如果你有两卡其实可以硬生生拆成两个独立服务让请求分摊出去# 第一个实例绑定GPU0端口11434 CUDA_VISIBLE_DEVICES0 OLLAMA_HOST0.0.0.0:11434 ollama serve # 第二个实例绑定GPU1端口11435 CUDA_VISIBLE_DEVICES1 OLLAMA_HOST0.0.0.0:11435 ollama serve两个实例可以加载同一个模型也可以加载不同模型。上层客户端轮询两个端口或者加一层负载均衡就能把请求分散到两张卡上。我实际测试过一种场景16并发请求、长上下文下去打一个模型实例响应时间会明显拉长甚至出现超时拆成两个实例后每个实例只有8并发排队时间立刻降下来。代价是要维护多个服务端口和模型副本复杂度高一些但对吞吐的改善是立竿见影的。3.4 一张卡装得下的模型开多卡通常只会更慢这是最反直觉的一点也值得单独拿来强调。许多人的第一反应是“卡越多越快”但在以Transformer层为单位做TP的推理场景下这句话是错的。我用一个7B模型做过简单对比单卡跑7B Q4生成速度约60 token/s强制双卡TP后速度反而掉到45 token/s左右。原因就是卡的互联带宽成为了瓶颈每一层都要做一次all-reduce通信通信开销大过了并行计算带来的收益。当模型本身一张卡就能装下时多卡没有任何正向帮助反而添乱。所以我的建议很明确能塞进单卡的模型就一定不要开TP。如果设置了OLLAMA_NUM_GPU0,1有些版本的Ollama可能会强行把模型拆到两张卡上哪怕显存够用。这时候反而应该显式指定CUDA_VISIBLE_DEVICES0让模型只在第一张卡上跑。4. 显存、量化与跨卡通信影响多卡性能的三个真正变量这一章回答一个核心问题我用TP把模型拆到多卡了为什么速度还是上不来除了卡数还有三个变量决定了最终体验。4.1 显存计算模型模型权重、KV Cache与运行时开销显存需求不是“模型文件多大就占多少”它由三部分构成模型权重基本等于GGUF文件实际大小比如Q4_K_M量化的70B模型大约40GBKV Cache与上下文长度成正比也和并发数成正比运行时开销激活值、临时缓冲区等。KV Cache的估算可以套一个简化公式KV Cache ≈ 2 × 层数 × KV头数量 × 头维度 × 上下文长度 × 每个元素字节数举例来说70B Q4_K_M模型配8K上下文时总显存需求大概在46~50GB左右。两张24GB的4090合计48GB看着凑合但几乎没有给并发和长上下文留余量。这也是为什么很多人在多卡跑大模型时一开长上下文就报显存不足或者卡到无法接受。6G显存的朋友更要注意6G卡比较稳妥的选择是7B或8B模型再量化到Q4比如qwen2.5:7b-instruct-q4_K_M同时把num_ctx限制在4K以内否则显存大概率会被KV Cache挤爆。4.2 跨卡通信带宽决定性能天花板TP模式下每一层Transformer的前向计算都需要一次跨卡同步通信量跟模型的hidden_size和batch大小直接相关。硬件层面的带宽差距非常悬殊互联方式典型带宽实际感受PCIe 4.0 x16双向约64GB/s小模型TP反而降速NVLink 3.0多卡最高约600GB/s大模型TP可用性明显提升多机以太网1~100Gb/s基本不适合做TPOllama要跨节点跑TP是不现实的它就是单机工具。多机场景下做法通常是在每台机器上各部署一个Ollama实例上层做请求分发而不是把模型切成十几份跨机器跑。实际调优时如果双卡TP比单卡加CPU offload只快一点点可以尝试缩小上下文长度或者把OLLAMA_NUM_PARALLEL调成1减少KV Cache后看看吞吐变化。另外较新版本的Ollama默认开启了flash attention这能显著减少KV Cache的读写压力间接缓解跨卡通信瓶颈。如果遇到性能异常也可以确认一下是否被旧版本或模型模板里的参数关掉了。4.3 和“GPU微调大模型”不是一回事别混淆方向网上搜索Ollama多GPU时经常会看到“GPU微调大模型”的内容但Ollama本身是做推理的不是训练框架。微调要的是数据并行、梯度同步那一套跑的是PyTorch、DeepSpeed、Megatron之类的训练栈而Ollama的多卡并行是推理侧的张量并行。这两个方向完全不同千万别拿着Ollama去干微调的活。如果你想微调直接转向训练框架如果只是想把大模型部署成API服务那Ollama多卡这套方案才是你要的。5. 多卡跑不动、卡不干活按这条链路排查多GPU排错最忌讳瞎猜。我总结了一条固定排查链路遇到问题按顺序走基本都能定位。5.1 排查主线日志、ollama ps、显存监控三连第一步先开debug日志OLLAMA_DEBUG1 ollama serve重点看启动阶段是不是把所有GPU都列出来了。如果日志里只有一个GPU问题大概率在环境层检查驱动、容器参数、CUDA_VISIBLE_DEVICES有没有误设置。第二步在模型加载后执行ollama psollama ps看PROCESSOR列和显存占用。如果显示模型加载在GPU上但没跨卡说明TP没生效或者模型太小单卡就装完了。第三步开着nvidia-smi dmon或nvtop观察实时利用率。注意区分“显存占用高”和“GPU利用率高”显存占用高只说明权重放到了显存里不代表计算在正常跑。如果显存占满了但利用率很低往往是CPU offload的层在拖后腿或者请求本身太小GPU饿着肚子等数据。5.2 常见症状与原因对照表症状可能原因解决方案模型加载了但第二张卡显存/利用率一直为0模型权重单卡就够放TP未生效确认OLLAMA_NUM_GPU换更大的模型或更低量化两张卡利用率都不高但响应很慢CPU offload层数过多跨卡通信瓶颈查看日志中offload层数降低num_ctx考虑NVLink设置了环境变量但完全没效果Windows服务/托盘进程没完全退出docker里没传环境变量彻底退出Ollama再重启docker加-e参数ollama run报file does not exist模型文件下载不完整或指定了不存在的标签删除本地模型后重新拉取ollama list确认标签下载模型非常慢默认模型仓库源不稳定下载GGUF后用Modelfile本地导入使用可用的模型下载加速方式系统盘被模型文件塞满模型默认存在~/.ollama/models设置OLLAMA_MODELS到数据盘迁移目录后重启6G显存到底能跑什么模型显存上限7B/8B Q4量化模型严格限制num_ctx这里面有两个点值得单独展开。第一环境变量不生效的问题在Windows上特别坑。你在系统设置里改了环境变量但Ollama是以系统托盘方式后台运行的不彻底退出托盘图标进程就不会重新读取新环境变量。改完一定先完全退出Ollama再重新启动。容器环境则更好办docker run的时候加-e OLLAMA_NUM_GPU0,1就行。第二模型下载慢和路径迁移。模型文件默认存放在~/.ollama/models长期跑下来系统盘很容易爆。迁移方式并不复杂先完全停止Ollama把整个目录移动到目标盘然后设置OLLAMA_MODELS/your/new/path再启动服务即可。另外如果官方源下载大模型慢到无法接受很多人会选择先用其他方式下载GGUF文件再通过Modelfile本地导入这在Ollama里是完全受支持的路径也能绕开下载慢的问题。5.3 一次完整推凶8卡机器为何只用上1张最后分享一个我自己处理过的案例完整还原排查链路。某次帮朋友调一台8卡服务器驱动的nvidia-smi一切正常8张卡全部识别docker也是--gpus all启动的结果Ollama加载70B模型后死活只用了1张卡。第一步我开OLLAMA_DEBUG1日志里果然只出现了一个inference compute id0。说明Ollama层面只看到一张卡问题不在模型调度而在更底层。第二步检查容器内GPU可见性进容器跑nvidia-smi发现容器里确实只看到1张卡。这就很蹊跷了因为宿主机上能看到8张命令也是--gpus all。最后翻了docker-compose配置才发现deploy.resources.reservations.devices里device_ids只列了一个0等于手动把GPU范围限制死了。改成device_ids: [0,1,2,3,4,5,6,7]之后Ollama日志里8张卡全部出现。本以为到此结束结果继续加载70B模型发现Ollama还是只把权重放到了GPU0。这次再检查是因为OLLAMA_NUM_GPU环境变量被某份旧教程改成了OLLAMA_NUM_GPU1——旧版本语义里的“只放1层到GPU”或“限制单卡”放到新版本里直接限制了GPU数量。清掉这个变量、改成显式指定OLLAMA_NUM_GPU0,1,2,3,4,5,6,7之后模型才真正跨卡加载。最后一步才是性能问题。8张卡虽然负载均衡了但有些卡之间走的是PCIe交换机没有NVLinkTP的通信延迟导致生成速度远低于预期。这时候已经不是“能不能跨卡”的问题而是“跨卡值不值”的问题。最终方案是把模型按卡分组拆成两个Ollama实例各吃4张卡吞吐才稳定下来。6. 顺手抄的配置模板和一点个人经验聊到这儿多卡跑Ollama的机制、玩法和排查思路都过了一遍。最后给一个可以直接抄的组合模板覆盖大部分单机多卡场景# 单模型跨卡TP多个模型同时驻留模型文件放数据盘 export OLLAMA_MODELS/data/ollama/models export OLLAMA_NUM_GPU0,1,2,3 export OLLAMA_MAX_LOADED_MODELS2 export OLLAMA_KEEP_ALIVE30m export OLLAMA_DEBUG0 ollama serve如果是纯并发服务场景可以换成多实例模板# 每卡一个实例 CUDA_VISIBLE_DEVICES0 OLLAMA_HOST0.0.0.0:11434 ollama serve CUDA_VISIBLE_DEVICES1 OLLAMA_HOST0.0.0.0:11435 ollama serve我自己的体会是多卡跑Ollama有两个极端一是别盲目追求“所有模型都跨卡”小模型单卡优先大模型才考虑TP而TP又得先确认卡间互联带宽够不够二是如果你在正经做服务多实例数据并行往往比单实例TP更实用因为它既不要求NVLink又能直接摊薄并发压力。最后再补一个小技巧任何一次变更环境变量后都养成ollama ps看一眼的习惯。它最能直观反映模型当前落在哪里、占了多少显存、哪个处理器在工作。多卡优化很多时候不是加配置而是减配置——把不必要的那张卡摘出去速度反而上来了。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →