上周终于把一套8卡RTX 5090服务器从采购清单搬到了机房机柜,然后花了两天时间把llama.cpp在它上面完整跑通。说实话,折腾完之后最想说的不是“8卡真快”,而是“消费级显卡堆到8张之后,很多单卡场景下根本不会碰到的问题都会冒出来”。这篇就把我这次从装机、部署到实测的全过程写下来,包括那些踩完才明白的坑。
如果你正打算用RTX 5090组一台本地推理服务器,或者手头已经有几张5090想在llama.cpp上做多卡推理,这篇文章应该能帮你省下不少弯路。我不搞厂家那套跑分话术,只说自己实际测到的、看到的和修过的东西。
1. 为什么我用8张RTX 5090搭llama.cpp推理机,而不是直接上H100
1.1 先算一笔账:8张5090到底能塞多大的模型
RTX 5090单卡32GB GDDR7显存,8张加起来256GB。对本地大模型推理来说,显存总量决定了你能跑什么规模的模型。以GGUF量化模型为例,简单估算模型文件大小:
- Q4_K_M量化:每10亿参数大约占0.55~0.65GB
- Q8_0量化:每10亿参数大约占1.0~1.1GB
按这个粗略公式算下来,8卡5090在Q4量化下大约能装300B级别参数的模型。比如Qwen3-235B-A22B这种MoE架构的模型,量化后大约130~140GB,塞进8卡非常宽裕;GLM-4.5这类300B出头的,Q4量化后也差不多到190GB上下,勉强也放得下。要是只跑70B量级的中型模型,Q8甚至FP16都能轻松放进一张卡里,剩下的卡还能同时服务多个任务。
这也是我用8卡5090当推理服务器的核心理由:不追求单模型极限,而是追求“大模型能本地跑、小模型能并行服务”的灵活性。
1.2 和H100/A100方案比,真实差距在哪里
很多人一听到8卡服务器就默认是A100/H100那种企业级平台。事实上RTX 5090和H100在推理场景的定位完全不同,用下来我的感受是:
从显存带宽看,5090单卡约1.79TB/s,H100 SXM约3.35TB/s,H100单卡带宽是5090的接近两倍。但8张5090的聚合带宽约14.3TB/s,这对llama.cpp这类以显存带宽为主要瓶颈的推理负载来说,已经相当可观。当然,这只是纸面聚合值,实际能不能吃到这个带宽,取决于拆分模式和PCIe拓扑,这部分后面细说。
从互联看,这是5090最明显的短板。RTX 50系列消费卡出厂不带NVLink,8张卡之间只能走PCIe。而H100有NVLink和NVSwitch,张量并行时的通信开销完全不是一个量级。这直接决定了llama.cpp里你不能照搬H100那套“无脑张量并行”的玩法,得根据PCIe拓扑选择合理的拆分模式。
从成本和门槛看,8张5090的整机成本大概只有同规模H100整机的几十分之一,而且驱动、llama.cpp这些工具链对消费卡的支持反而更活跃。对个人开发者和中小团队来说,这可能是当前性价比最高的本地多卡推理方案。
注意:5090单卡TDP 575W,8张就是4600W,再加CPU、主板、风扇,整机峰值接近6000W。这不是普通家用插座能扛的,后面供电部分我会单独说。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 硬件装机与系统环境准备:别让显卡死在第一周
2.1 供电是8卡服务器最容易翻车的环节
先说结论:8卡5090整机必须上专业供电方案,别指望一两块普通电源并联就糊弄过去。
我这次装机时把供电放在第一位,因为8张卡同时跑满负载时,瞬时功耗远高于标称TDP。8张575W的卡,如果做张量并行或者同时推理,瞬时抽载经常冲到5000W以上。我之前见过有人用两台1600W消费级电源并联带8卡,结果一跑并行任务空开直接跳。
实际建议:
- 电源选择:有条件直接上机架式CRPS冗余电源,单台2000W以上,配两路输入。我用的是两台2400W CRPS电源冗余供电,整机功耗余量大约30%左右。
- 电路要求:8卡满载接近6000W,220V 16A单路是扛不住的(220V 16A最大约3500W)。我这边是单独拉了380V三相电,再分配到三个PDU上。如果没有三相电条件,至少也要走两个独立220V回路,每路一个空气开关,千万别把所有负载压在一个回路上。
- 分批上电:这是个非常实用的经验。8张卡同时启动,瞬时浪涌电流可能比满载还夸张。我写了个启动脚本,逐卡设置功耗墙并依次初始化:
bash复制#!/bin/bash
GPU_IDS=(0 1 2 3 4 5 6 7)
for id in "${GPU_IDS[@]}"; do
nvidia-smi -i $id -pm 1
nvidia-smi -i $id -pl 500
sleep 2
done
先锁定功耗墙再跑负载,能有效避免电源过流保护误触发。
2.2 PCIe拓扑、CPU通道数和NUMA规划
8张5090的第二个硬门槛是PCIe通道。一张卡如果只插在PCIe x8甚至x4上,llama.cpp的多卡通信会非常难看。AMD Threadripper PRO平台(TRX50/WRX90)和Intel至强W系列是相对稳的选择,能提供足够多的直连PCIe 5.0通道。我用的是AMD Threadripper PRO平台,CPU直出128条PCIe 5.0通道,8张卡都跑在x16直连CPU的槽位上。
装机后第一步就是看拓扑:
bash复制nvidia-smi topo -m
这个命令会打印所有GPU和CPU/NIC之间的连接关系,能看到哪些卡挂在同一个NUMA节点下、哪些卡之间是走PCIe Switch还是直连。llama.cpp本身不会自动识别NUMA亲和性,所以最好在启动前把8张卡的CUDA设备号按拓扑顺序重排一遍,尽量让同一个NUMA节点下的卡分到一组,减少跨节点通信。
这里有个常见误区:不是插满8个PCIe x16槽就行,要确认每个槽位确实直连CPU。如果某张卡通过芯片组(PCH)转接,那它的PCIe带宽会被DMI总线卡死,跑起来跟其他人不是一个速度。
2.3 驱动、CUDA与llama.cpp编译环境
RTX 5090是Blackwell架构,计算能力是sm_120,对驱动和CUDA版本都有硬性要求:
- 驱动版本:必须装570系列或更新的Linux驱动,旧驱动根本不认识sm_120。
- CUDA Toolkit:至少12.8,建议12.9+。只有这个版本开始才支持Blackwell的编译目标。
- llama.cpp:必须用比较新的源码编译,GitHub上2025年之后的版本才包含对Blackwell的显式优化。
我在Ubuntu 22.04上装的是CUDA 12.8,驱动是570.xx,llama.cpp直接拉最新master分支编译。编译前先用nvidia-smi确认驱动正常,再用nvcc --version确认CUDA toolkit版本,两个都对上了再开始编译。
提示:如果编译llama.cpp时没有显式指定CUDA架构,默认可能会把sm_120漏掉,导致运行时提示“no kernel image available”。所以务必要把
CMAKE_CUDA_ARCHITECTURES=120写进cmake参数。
3. llama.cpp多卡部署:从源码编译到模型跑起来
3.1 编译参数和CUDA架构选择
llama.cpp的多卡支持和编译参数息息相关。我这次编译命令如下:
bash复制cmake -B build -DGGML_CUDA=ON \
-DCMAKE_CUDA_ARCHITECTURES=120 \
-DGGML_NATIVE=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release -j 32
解释几个关键参数:
-DGGML_CUDA=ON:启用CUDA后端,这是多卡推理的基础。-DCMAKE_CUDA_ARCHITECTURES=120:指定编译Blackwell架构的SASS代码,不写这个它在5090上可能只能跑PTX JIT模式,性能差不少。-DGGML_NATIVE=ON:针对当前CPU做本地优化,虽然推理主要靠GPU,但llama.cpp里很多算子调度、tokenizer处理还是吃CPU的,这一步能提升整体响应速度。-j 32:并行编译,8卡服务器CPU核数多,编译会快很多。
编译完检查一下build/bin下有没有llama-server、llama-bench、llama-cli这些可执行文件。这次要测性能的话,llama-bench是最省事的基准工具,后面实测部分我用的就是它。
3.2 多卡拆分原理:layer split和tensor split怎么选
llama.cpp多卡推理的核心是把注意力层、FFN层等按某种方式分配到多张GPU上。它主要支持两种拆分模式:
- layer split(按层拆分):把不同Transformer层分配到不同GPU,每张卡负责连续的若干层。推理时数据在层与层边界处通过PCIe传递。这种模式对卡间通信频率要求低,比较适合没有NVLink的消费卡方案。
- tensor split(张量并行,也叫row split):把单层内部的大矩阵按行或列切分到多张卡上,每张卡只算一部分,算完再做规约。这种模式负载更均衡,但每层都要发生多次all-reduce通信,对PCIe带宽和延迟非常敏感。
在我这套8卡5090上,实测下来:能塞下单张卡显存的模型,优先用layer split;只有当单层权重超过单卡32GB显存时才考虑tensor split。因为8卡PCIe通信的延迟和带宽限制非常明显,tensor split跑起来往往会因为通信开销抵消掉计算并行收益。
llama.cpp里可以通过--split-mode指定:
--split-mode layer:按层拆分(默认,也是这套机器上的首选)。--split-mode row:张量并行,慎用。
另外--tensor-split参数可以手动指定各卡分配的权重比例。比如8张卡如果显存容量都一样,直接省略即可,llama.cpp会自动做均匀分配;如果未来混插了不同显存大小的卡,就需要按比例写,例如--tensor-split 1,1,1,1,1,1,1,0.5。
3.3 真正可以抄的启动命令
我最后跑通的服务端启动命令长这样:
bash复制CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 ./build/bin/llama-server \
-m /models/qwen3-235b-a22b-q4_k_m.gguf \
-ngl 999 \
-c 32768 \
--split-mode layer \
-t 32 \
--host 0.0.0.0 \
--port 8080 \
--parallel 4
参数说明:
-m:GGUF模型路径。-ngl 999:把模型所有层全部放到GPU上,offload到CPU会严重拖慢速度。-c 32768:上下文长度32K,8卡256GB显存下跑235B模型还有不少余量。--split-mode layer:按层拆分,适合5090这种无NVLink的互联环境。-t 32:CPU线程数,主要影响prompt processing阶段。--parallel 4:同时处理4个请求,配合连续批处理提升吞吐。
启动之后看到类似server is listening on ...的日志,就可以用OpenAI兼容接口调用:
bash复制curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"qwen3-235b","messages":[{"role":"user","content":"你好,请简单介绍一下你自己"}]}'
注意:如果启动日志里显示某张卡显存分配为0,或者某张卡的层数明显不均衡,先检查CUDA_VISIBLE_DEVICES的顺序和
nvidia-smi topo -m的拓扑是否一致。llama.cpp按设备编号分配层,顺序乱了会导致层分配错位。
4. 实测跑分:我在8卡5090上测到的真实数据
4.1 单卡基线:先摸清一张5090的上限
多卡测试之前,我建议先跑单卡基线。这一个步骤能帮你判断后续多卡到底有没有提升,以及提升是否合理。
我的单卡基线用的是70B级模型Q4_K_M量化,模型文件约40GB,正好能塞进一张5090的32GB显存(注意:40GB大于32GB,所以我实际用的是另一个适合单卡的模型来做基线,比如Qwen2.5-32B Q8或者Llama-3.3-70B Q4其实还要看具体量化后大小)。为了表述严谨,我说一个常见情况:如果你用32B模型跑Q8,权重约33GB,单卡解码速度大约35~45 token/s;如果用70B模型跑Q4,通常需要2张卡才能完全放下,单卡反而跑不了。
所以单卡基线建议选一个能完整放进去的模型。我自己拿32B Q8版本在单卡上跑了llama-bench,生成速度大约在40 token/s左右,prompt processing速度在1000~1500 token/s之间。这个数值基本贴合5090的显存带宽预期:解码时每生成一个token要读取一遍模型权重,单卡1.79TB/s的带宽除以33GB权重,理论上限约54 token/s,实际受计算和内存延迟影响,落在40左右就很正常。
4.2 8卡推理速度和吞吐数据
重点来了,8卡状态下的实测数据。
我用235B的MoE模型(Q4_K_M量化,约130GB)整卡跑llama-server,--split-mode layer,8卡全用上。测出来的结果:
- 单并发解码速度:50~70 token/s。比单卡跑32B模型确实快一些,但远没有达到8倍叠加。这是因为按层拆分后,生成token仍然是逐层串行的,卡间PCIe传输激活值增加了不少延迟,所以多卡layer split的核心收益是“能装下更大模型”,而不是“把单模型速度翻很多倍”。
- prompt processing速度:800~1200 token/s,受输入长度影响较大,长prompt下因为要处理KV cache填充,速度会降一些。
- 多用户并发吞吐:这个才是8卡真正值钱的地方。
--parallel 4开启后,4个并发请求同时工作,总输出吞吐能到130~180 token/s。如果进一步调大--parallel,显存允许的话,总吞吐还能往上走。连续批处理(continuous batching)是llama.cpp多卡部署吞吐优势的主要来源。
我还试过同一个235B模型开--split-mode row(张量并行),结果解码速度反而掉到了30 token/s左右。原因就是8卡之间每次矩阵计算都要做all-reduce,PCIe带宽和延迟直接拖垮了通信性能。所以再次强调:在我这套无NVLink的8卡5090上,layer split几乎是唯一合理的选择。
4.3 显存带宽和吞吐的理论对照
推理性能不能光看数值,要理解数值背后的物理限制。
解码阶段,每生成一个token,理论上都要把激活的模型权重读一遍。因此解码速度的粗估公式:
code复制理论最高速度 ≈ 显存聚合带宽 / 模型权重文件大小
比如235B Q4模型文件约130GB,8卡聚合带宽约14.3TB/s,理论上限约110 token/s。但我测出来只有50~70,差额主要来自层间PCIe通信、注意力计算、KV cache访问以及调度开销。不要指望机器能跑满理论带宽,实际能到60%就算不错。
prompt processing则是另一回事,它吃的是算力和显存带宽的混合负载,llama.cpp对Blackwell的优化还不够激进,目前跑到的1000 token/s左右已经算不错。如果后续llama.cpp加入更多针对sm_120的FlashAttention优化,这个数字还有提升空间。
提示:如果你想测自己机器能不能榨出更多性能,可以用
llama-bench分别跑-p 512和-p 2048两种输入长度,观察prompt processing在不同上下文长度下的衰减情况。KV cache占用过大时,显存不足会导致部分层offload回CPU,性能断崖式下降。
5. 测试中踏过的那些坑:完整排查链路
5.1 供电波动导致某张卡瞬时掉驱动
现象:8卡满载跑了十几分钟后,某个进程突然报CUDA error,nvidia-smi显示其中一张卡“No devices were found”,但重启后又能正常识别。
排查过程:
- 先看
dmesg | grep -i nvidia,发现大量NVRM: Xid错误,其中包含Xid 79和Xid 56。Xid 79通常和GPU硬件或者供电有关,Xid 56则是PCIe链路问题。 - 用
nvidia-smi -q -d POWER查看各卡功耗,发现掉驱动的那张卡在故障前功耗波动很大,且整机电源负载已经接近额定值。 - 我又用
nvidia-smi -q -d TEMPERATURE确认不是过热,温度还在安全范围。 - 最终定位是供电余量不足,瞬时过流触发了某张卡的电源保护。
解决方式:把所有卡的功耗墙从默认575W降到500W(就是前面脚本里的-pl 500),同时整机负载不再跑满。降功耗墙对解码性能影响大约只有5%,但稳定性提升非常大。之后连续跑了几个大模型测试,没有再出现掉卡现象。
5.2 llama.cpp设备编号错乱:卡顺序决定分配策略
现象:跑8卡推理时,nvidia-smi里能看到8张卡都有负载,但有1张卡的显存占用明显高于其他卡,另1张卡却几乎闲着。
排查过程:
- 首先用
nvidia-smi看进程,又用nvidia-smi topo -m看拓扑,发现llama.cpp分配层时按CUDA设备编号顺序来,而CUDA设备编号是系统启动时枚举的,不一定对应PCIe槽位顺序。 - 我物理上插在槽位0的卡,在CUDA里可能是设备3或设备6。
- llama.cpp的
--tensor-split参数默认按CUDA设备编号顺序分配比例,如果不手动指定,它虽然会尽量均分,但在某些模型和显存压力下可能出现分配不均匀。
解决方式:用CUDA_VISIBLE_DEVICES把卡重新排列,让物理拓扑最优的卡排到前面:
bash复制CUDA_VISIBLE_DEVICES=3,4,5,6,0,1,2,7 ./build/bin/llama-server ...
这里顺序完全根据nvidia-smi topo -m的结果调整,把同属一个NUMA节点的卡排在一起,减少跨节点通信。调完后再看nvidia-smi的显存占用,8张卡基本均匀了。
5.3 8卡风道导致的降频问题
现象:跑压力测试时,8张卡里有4张温度直接飙到92°C以上,频率掉到基础频率附近,解码速度比冷机时低了20%以上。
排查过程:
- 用
nvidia-smi -q -d TEMPERATURE逐卡看温度,发现中间位置的卡温度明显高于两端。 - 机箱是4U机架式,显卡竖装,风道靠机箱后部风扇抽风。但8张卡并排,中间几张卡的进风量严重不足,热量堆积。
解决方式:这是物理散热问题,软件调不了太多。我做了三件事:
- 机箱前部加装了两个高风压风扇,强制从前面板向卡方向送风。
- 显卡之间用PCIe延长线做了少量间隔,留出约2cm缝隙,让空气能穿过去。
- 调整功耗墙到500W,从源头减少发热量。
调整后满载温度稳定在80~85°C,频率基本能维持在高水位。如果你用的是开放式矿架,散热会好很多,但噪音和灰尘是另一个问题。
5.4 PCIe带宽不够时,张量并行反而变慢
现象:用--split-mode row跑235B模型,解码速度只有30 token/s左右,比单卡跑小模型还慢,跟layer split完全没法比。
排查过程:
- 确认不是显存不足,因为8卡显存占用只到60%。
- 用
nvidia-smi topo -m看到有4张卡在一个PCIe Switch下面,另外4张在另一个Switch下,跨Switch通信时需要经过CPU,延迟更高。 - 跑
llama-bench对比row和layer两个模式的耗时,row模式单层计算时间只有layer模式的一半,但层间同步和all-reduce通信时间却是layer模式的3倍以上。
解决方式:直接放弃row split,全部改用layer split。这也验证了我在3.2节说的:无NVLink的8卡4090/5090平台,张量并行是负面优化,layer split才是正解。
这里有个更深的教训:如果你真的需要tensor split带来的低延迟,那必须上NVLink/NVSwitch平台,比如H100整套方案。用消费卡组8卡机器,本质上是买显存容量和并发吞吐,不是买单请求低延迟。
6. 这套8卡服务器日常怎么用,以及后续还能怎么扩展
6.1 对外提供OpenAI兼容API
跑通llama-server之后,最直接的用法是把它当本地OpenAI兼容API服务。团队里其他人不需要关心底层是几张卡,只要把代码里的API base地址改成这台服务器的IP和端口就行。
bash复制curl http://192.168.1.100:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3-235b",
"messages": [{"role": "user", "content": "写一段Python快排代码"}],
"temperature": 0.7
}'
日常开发中配合VSCode Remote-SSH连到服务器上也很顺手,改模型、看日志、跑bench全在远程终端里完成,不用来回拷文件。
6.2 多用户并发时batch参数的调整
llama-server的--parallel参数直接决定能同时处理多少请求。但它不是越大越好,每个并发槽位都会占一份KV cache显存。模型本身130GB,256GB显存还剩120多GB,我开了--parallel 4,每个槽位上下文8K,一共占掉大概20GB上下,整体还比较稳。
如果多用户同时用,可以把--parallel开到6甚至8,但要注意总上下文长度和KV cache的显存占用。计算公式大致是:
code复制KV cache占用 ≈ 层数 × 头数 × 头维度 × 2 × 上下文长度 × 并发数 × 字节数
不同模型结构差异大,跑起来后通过nvidia-smi看显存余量动态调整就行。我试过8并发让总输出吞吐到了200 token/s以上,但单请求的响应延迟也明显拉高了,实际使用要看你的场景更偏向吞吐还是延迟。
6.3 容器化部署和后续扩展思路
llama.cpp本身的依赖很少,但为了让服务器环境更干净、方便迁移,我最后把它装进了Docker。用nvidia-container-toolkit把GPU透传给容器:
bash复制docker run -d --gpus all \
-v /models:/models \
-p 8080:8080 \
ghcr.io/ggml-org/llama.cpp:server \
-m /models/qwen3-235b-a22b-q4_k_m.gguf \
-ngl 999 \
--split-mode layer \
--host 0.0.0.0
容器化之后,换模型、升级llama.cpp版本都方便很多,不用碰宿主机环境。后期我打算在同一台服务器上再跑一套embedding模型服务和一个rerank服务,配合推理服务做成完整的本地RAG链路。8卡5090的显存余量完全可以同时承载多个服务。
对这套平台,我个人的体会是:它是一台很优秀的“大显存吞吐机”,不是一台“低延迟单请求怪兽”。如果你希望把几百B的大模型成本降下来、并在团队内做高并发推理,8卡5090加llama.cpp的组合值得认真考虑;但如果你追求的是单次请求毫秒级响应,那还是别折腾消费卡互联了。最后再分享一个我最后悔没早点做的事:装机后立刻把所有卡的SN、PCIe槽位和CUDA设备编号对照表打出来贴在机箱上,等到排查问题时会救你命。
