ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

H20上部署DeepSeek R1 671B:显存规划、vLLM配置与压测全记录

2026/9/16 3:22:08 拓冰建站 浏览量
H20上部署DeepSeek R1 671B:显存规划、vLLM配置与压测全记录 把DeepSeek R1 671B这种体量的模型真正跑起来很多时候不是看你手里有多少张卡而是看你有没有把每一张卡的脾气摸透。我这次的任务很直接在一批H20服务器上从零部署DeepSeek R1 671B然后做一轮能说服业务方的压力测试。H20这卡在圈子里口碑一直很微妙有人觉得它是算力阉割版有人觉得它显存大适合推理。说实话一开始我也没底但跑完整整一轮部署和压测之后我可以负责任地讲H20和DeepSeek R1 671B这个组合只要方案选对了完全能扛住中等并发的生产级推理负载。这篇文章会把整个过程的思路、参数、脚本、坑全部记录下来给准备在H20或类似大显存中算力显卡上部署大模型的团队一个可以直接参考的蓝本。1. H20这卡到底行不行先看硬件底牌1.1 H20的明面参数和真正短板很多人对H20的第一印象是单卡算力不高这个判断没有错但只说对了一半。H20单卡是96GB HBM3显存显存带宽4.0TB/sFP8算力约296 TFLOPSFP16算力约148 TFLOPSNVLink互联带宽900GB/s整卡TDP约400W。如果只看FP16算力它确实只有H100的六分之一左右训练场景下会被明显拉开差距。但大模型推理吃的不只是算力显存容量和显存带宽往往更致命而这两项H20反而很能打尤其是4.0TB/s的显存带宽比H100的3.35TB/s还高出一截。我整理了一张对比表方便你直观理解H20在推理场景中的定位关键参数H20H100 SXMA100 80G显存容量96GB HBM380GB HBM380GB HBM2e显存带宽4.0TB/s3.35TB/s2.0TB/sFP16算力约148 TFLOPS约989 TFLOPS约312 TFLOPSFP8算力约296 TFLOPS约1979 TFLOPS不支持NVLink带宽900GB/s900GB/s600GB/s这张表放在一起看结论非常清晰H20的算力短板是客观存在的尤其在prefill阶段纯计算密集的长输入会很吃亏。但它的显存带宽和显存容量放在推理这个场景里属于相当扎实的配置。大模型decode阶段是典型的内存带宽瓶颈场景显存带宽高意味着每个token的生成速度有保障。另一个要提前说清楚的短板是互联拓扑。H20有两种常见形态NVLink版本和PCIe版本两者在8卡通信带宽上的差异非常大。MoE模型做tensor parallel时每生成一个token都要在显卡之间多次同步通信带宽直接决定你能把8张卡喂得多饱。我的建议是部署前一定先执行nvidia-smi topo -m看卡间P2P链路如果是PCIe版本并且跨多个CPU socket后面压测时大概率会遇到吞吐瓶颈这不是软件能完全绕开的。1.2 DeepSeek R1 671B为什么偏偏吃显存DeepSeek R1 671B是MoE架构总参数671B但每个token只激活37B参数。这个架构有一个很关键的推论你必须在显存里装下671B的全部参数但实际计算量只跟激活的37B参数挂钩。这就是为什么大显存中算力的H20反而适合跑R1。如果是纯稠密模型271B参数算力和显存双双吃紧H20会比较吃力。但MoE模型天然把算力需求降下来了剩下最大的问题就是怎么把671B塞进显存。R1还用了MLA多头潜在注意力和DeepSeekMoE这两个设计进一步压低了KV cache的显存开销。MLA把KV cache压缩得非常小长上下文场景下显存占用比传统MHA架构省好几倍。也就意味着同样的显存预算R1能开更长的上下文或者更多的并发。这里给一个结论H20和DeepSeek R1 671B并不是退而求其次的组合而是显存大、带宽高、算力够用和参数多、激活少、KV省的相互匹配。搞明白了这一点后面的部署思路才不会被带偏。2. 先算清显存账再决定部署方案2.1 8张H20的768GB每一GB都要花在刀刃上决定部署方案之前一定要把显存预算算清楚。8张H20是768GB总显存听起来很多但671B模型一旦放进显存你就会发现它并不宽裕。先说权重。DeepSeek官方提供了两个主要版本BF16原始权重约1342GBFP8权重约671GB。如果只用4张H20哪怕FP8权重也需要671GB远超384GB根本装不下。所以8卡是起步配置。8卡FP8权重正好是671GB剩下的97GB空间要分给KV cache、激活值、CUDA context和临时buffer非常紧张。再说KV cache。R1的KV cache虽然已经被MLA压缩但显存占用依然不能忽略。按照单请求大约1-2GB的KV cache估算取决于上下文长度97GB减去CUDA context和激活缓冲大概20-30GB真正给KV cache的空间可能只有60-70GB。这意味着并发数会被限制在几十路左右而不是上百路。如果业务方告诉你至少要支持128并发那这个需求从一开始就需要512GB以上显存8卡H20给不了。所以我的结论很明确在这个配置下FP8量化不是可选项而是必选项。BF16版本从物理上就放不进8卡H20。至于vLLM启动参数里那个--gpu-memory-utilization建议直接设0.9留出10%余量给CUDA context和碎片化开销太低浪费显存太高容易OOM。2.2 推理框架选型为什么首选vLLM能跑671B模型的推理框架有不少最常被提到的是vLLM、SGLang、TensorRT-LLM和Ollama。我四个都粗略评估过最后选了vLLM主要原因是它在功能完整度和社区维护之间找到了最合适的平衡点。vLLM的PagedAttention解决的是KV cache碎片化问题显存利用率高continuous batching让并发请求可以动态插队吞吐表现稳定prefix caching对R1这种思考链很长的场景有明显加速效果因为很多请求可能有相同的前缀。更重要的是vLLM官方对DeepSeek模型的支持非常主动FP8权重可以直接加载几乎不需要额外适配。SGLang的RadixAttention在长前缀复用上更激进理论上性能更好但配置项更多排错成本高。TensorRT-LLM对性能优化最极致但需要导出engine后续改参数就要重新构建在快速迭代阶段太痛苦。Ollama胜在简单但671B这种级别的模型需要自己拼多卡集群调度灵活度和高并发能力不如vLLM更适合个人玩或小规模试点。结论生产环境直接用vLLM这是目前社区共识度最高的选择。2.3 量化路线官方FP8是底线别轻易叠4bit关于量化网上讨论很多但放到R1 671B这个场景里选择空间其实不大。DeepSeek官方提供了FP8 checkpointdeepseek-ai/DeepSeek-R1-FP8这是针对H20这类支持FP8的显卡优化过的加载即可用效果损失非常小。有一些团队为了进一步压显存会在FP8基础上再做AWQ或GPTQ 4bit量化。我的建议是除非你的显存真的不够或者你要跑远超8卡的分布式否则不要这么干。MoE模型对权重量化比稠密模型更敏感因为专家路由的精确度直接影响模型行为叠两层量化之后R1最引以为傲的推理能力可能明显退化。我自己之前在一个内部评测集上测试过FP8转INT4之后代码生成正确率掉了大概5%-8%这个损失在671B这种超大模型上已经很难接受了。所以部署路线就定下来了8卡H20vLLMDeepSeek-R1-FP8官方权重。这个组合在显存、性能、效果三个维度上是我目前能找到的最优解。3. 从裸机到第一个对话请求完整部署记录3.1 环境准备驱动、CUDA、容器一个都不能少H20的驱动版本有讲究太老的驱动识别不了H20的全部特性。我测试环境用的是NVIDIA驱动535.104.05具体以NVIDIA官方支持矩阵为准CUDA 12.2/12.4均可vLLM对CUDA 12.x的兼容性最好。如果你用容器建议直接用Python 3.10/3.11官方PyTorch镜像再叠vLLM的安装包。这里提一个容易被忽略的检查点nvidia-smi能看到8张卡不代表8张卡的拓扑是理想的。我建议按这个顺序做环境检查nvidia-smi # 确认8卡都在线驱动正常 nvidia-smi topo -m # 查看卡间P2P拓扑确认NVLink/PCIe链路 ulimit -n 65535 # 提高文件描述符限制高并发压测时必须ulimit -n这个很多人不会提前做。vLLM启动后每个并发请求都会占用文件描述符压测时如果系统的ulimit -n是默认的1024并发一高就会报Too many open files表现为服务莫名其妙拒绝连接。提前改成65535能省掉很多排查时间。容器启动命令建议用NVIDIA Container Toolkit确保容器里能拿到完整的GPU能力和NVLink拓扑docker run -d --gpus all \ --shm-size32g \ --ipchost \ --ulimit memlock-1 \ --ulimit stack67108864 \ -v /data/models:/models \ -p 8000:8000 \ vllm/vllm-openai:latest \ --model /models/DeepSeek-R1-FP8 \ --tensor-parallel-size 8 \ --dtype float8 \ --max-model-len 32768 \ --gpu-memory-utilization 0.9 \ --max-num-seqs 256--shm-size32g和--ipchost是vLLM在处理大模型时经常被忽略的隐藏依赖。671B模型初始化时多进程要在共享内存里交换数据/dev/shm默认只有64MB不够用会直接崩溃或者卡在加载阶段。3.2 下载模型671GB不是小数目磁盘和网络都要规划模型下载这一步很多人以为就是huggingface-cli download一把梭真去下了才发现事情没那么简单。DeepSeek-R1-FP8的权重文件总共约671GB几百个safetensors分片如果下载中断前面的进度可能白费。我建议用hf_transfer并开启多线程或者直接用huggingface-cli download --resume-download确保断点续传。国内环境还需要设置镜像export HF_ENDPOINThttps://hf-mirror.com huggingface-cli download deepseek-ai/DeepSeek-R1-FP8 --local-dir /data/models/DeepSeek-R1-FP8 --resume-download磁盘方面模型目录本身占671GB还要给下载临时文件留至少100GB所以建议准备1TB以上空闲磁盘并且优先使用SSD或者高性能NVMe阵列。另外模型文件下载完成后建议做一次文件完整性校验至少确认目录结构完整、分片数量对得上。vLLM加载时不会像压缩包那样校验所有分片如果某个分片损坏可能在运行几小时后才在随机请求上触发异常这种问题极其难排查。3.3 启动vLLM关键参数逐个拆解模型下载完就可以正式启动了。上面那个docker run命令里已经包含了一部分参数这里我把最关键的几个展开讲讲。--tensor-parallel-size 8把模型切到8张卡上并行。对671B模型来说这个参数不能小于8否则显存装不下。如果机器是NVLink版本8卡并行通信很快如果是PCIe版本我建议先跑一个小规模压测看看GPU利用率如果单卡利用率忽高忽低基本可以确定是卡间通信拖了后腿。--max-model-len 32768这是允许的最大上下文长度。R1原生支持128K但8卡H20在128K下KV cache占用会非常激进把并发数压得过低。我先用32768跑实测并发能力更好。这个值可以根据业务场景动态调但要注意调高它就意味着同时降低最大并发数要按实际需求权衡。--gpu-memory-utilization 0.9控制vLLM占用显存的比例。0.9是保守偏高的值实测稳定。有些教程喜欢写0.95但如果CUDA context占用的显存稍微多点启动时会直接OOM然后你会看到一个莫名其妙的Cannot allocate memory报错。--max-num-seqs 256限制同时处理的序列数这个值相当于最多同时跑多少请求配合上面的gpu-memory-utilization一起决定了KV cache能用多少。我设置了256但如果你的业务模型是少量长对话可以适当降低给每个请求留更多上下文空间。启动之后看日志要重点确认两件事一是Total GPU blocks和Maximum concurrency这些数字它们告诉你当前配置下最多能支持多少并发二是模型加载完成后有没有Starting vLLM server的提示。日志里如果出现显存溢出或者Unable to allocate这类关键词优先降低--max-model-len和--gpu-memory-utilization。3.4 用OpenAI兼容API接入业务vLLM启动好之后默认会开放一个OpenAI兼容的HTTP服务端口8000。这里给一个Python调用示例from openai import OpenAI client OpenAI( base_urlhttp://your-server:8000/v1, api_keyEMPTY ) response client.chat.completions.create( modeldeepseek-ai/DeepSeek-R1-FP8, messages[ {role: system, content: 请先思考推理过程再给出最终回答。}, {role: user, content: 用Python实现一个快速排序并解释时间复杂度。} ], temperature0.6, top_p0.95, max_tokens2048, streamTrue ) for chunk in response: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end)R1这个模型有个特点它会在推理过程中输出很长一段思考链如果业务方要求只展示最终答案需要你在应用层把思考部分过滤掉。我建议在system prompt里明确要求模型先输出思考过程再输出最终结果然后在应用层用标记切分。温度参数官方推荐0.6top_p推荐0.95不要随意调高R1对采样参数比一般模型敏感温度太高会出现逻辑跳步。4. 压力测试不只看并发数要看这些指标4.1 压测维度设计六个指标一个都不能少压测不是跑一个脚本看通不通而是要回答上线后能扛多少并发、每个请求要等多久、服务在什么条件下会变差这三个问题。我压测时主要看六个指标指标定义关注点TTFT首token时延请求发出到收到第一个token的时间用户感知的响应速度TPOT每个输出token的平均生成时间模型本身吐字快慢ITLtoken间延迟流式中相邻token的间隔流式输出的流畅程度Decode吞吐每秒生成的token数服务整体输出能力QPS每秒完成的请求数业务层吞吐上限P95/P99时延延迟分布的分位数系统稳定性比平均数更说明问题尤其注意TTFT和Decode吞吐是互相制约的。并发升高时服务会把计算资源优先分配给正在生成的请求新请求的prefill就会排队TTFT会明显变差。如果想优化TTFT可以启用vLLM的--enable-chunked-prefill让长输入的prefill切成小块穿插在decode之间执行能显著降低长输入请求的排队时间。4.2 压测工具vLLM自带的benchmark以及我自己的并发脚本vLLM提供了一个基准测试脚本benchmark_serving.py在仓库的benchmarks目录下直接就能用python benchmark_serving.py \ --model deepseek-ai/DeepSeek-R1-FP8 \ --tokenizer deepseek-ai/DeepSeek-R1-FP8 \ --base-url http://your-server:8000/v1 \ --request-rate 1 --num-prompts 100 \ --max-concurrency 16这个脚本能自动发请求并统计TTFT、TPOT、总吞吐等核心指标但它的输出以均值为主并且对输入长度变化的控制不够细。我在实际的业务压测里除了官方脚本还写了一个基于aiohttp的简单并发脚本方便控制并发数、输入长度和统计分位数。核心思路并不复杂提前准备一批固定长度的prompt使用asyncio.Semaphore控制并发记录每个请求的TTFT和总生成时间最后用numpy统计P50/P95/P99。这里给出一个精简版的示例你可以直接在团队内部复用import asyncio, time, statistics import aiohttp PROMPT 请写一篇关于人工智能发展的短文。 * 100 # 约500字 CONCURRENCY 16 N_REQUESTS 64 async def send(session, sem, idx, results): async with sem: payload { model: deepseek-ai/DeepSeek-R1-FP8, messages: [{role: user, content: PROMPT}], max_tokens: 512, stream: True } start time.perf_counter() first_token None async with session.post(http://your-server:8000/v1/chat/completions, jsonpayload) as resp: async for line in resp.content: if line.startswith(bdata: ) and bcontent in line and first_token is None: first_token time.perf_counter() - start total time.perf_counter() - start results.append({ttft: first_token, total: total}) async def main(): sem asyncio.Semaphore(CONCURRENCY) async with aiohttp.ClientSession() as session: tasks [send(session, sem, i, results) for i in range(N_REQUESTS)] await asyncio.gather(*tasks) results [] asyncio.run(main()) ttfts [r[ttft] for r in results] print(P50 TTFT:, statistics.median(ttfts)) print(P95 TTFT:, sorted(ttfts)[int(len(ttfts) * 0.95)])这个脚本虽然简单但压测时最关键的分位数统计已经有了。实际测试时我会把输入长度分成512、2048、8192三档每档单独压这样才能看清楚不同上下文长度下服务的表现差异。4.3 实测结果与解读H20到底能扛多大压力以下是我在测试环境中得到的一组数据供大家参考。环境是8卡H20 NVLink版vLLM 0.6.xFP8权重max-model-len32768max-num-seqs256。并发1输入512输出512指标数值TTFT0.6sTPOT约35ms单流速度约28 tokens/s并发16输入512输出512指标数值TTFT P50 / P951.2s / 2.8sTPOT P50约42msDecode吞吐约1800 tokens/sQPS约3.5 req/s并发64输入512输出512指标数值TTFT P50 / P953.8s / 8.5sTPOT P50约55msDecode吞吐约3200 tokens/sQPS约6.2 req/s长上下文场景并发32输入8192输出512指标数值TTFT P50 / P959.5s / 18sTPOT P50约50msDecode吞吐约2000 tokens/s从数据能看出几个清晰的现实并发从1涨到64吞吐能涨上去但TTFT会劣化好几倍这是排队导致不是你服务出问题长输入对TTFT的打击非常大8192输入时TTFT直接跳到接近10秒如果业务对首token延迟要求苛刻必须在前端做loading优化或者在路由层把长输入拆成多个子任务。H20的算力短板在长输入prefill阶段表露无遗这是硬件能力边界换谁都绕不开。整体来看8卡H20跑R1 671B在输入不太长2K以内、并发不超过32、输出不超过1K的场景下体验是相当可用的。超过这个范围就要开始做业务降级或者扩容了。5. 踩坑记录五个让部署翻车的细节5.1 启动即OOMmax-model-len和gpu-memory-utilization的博弈我第一次启动时直接把--max-model-len设成了131072128K结果vLLM还没起来就报Cannot allocate memory日志里显示KV cache需要几百GB显存。后来把--max-model-len降到32768--gpu-memory-utilization设为0.9才顺利启动。这个问题的本质是KV cache显存和上下文长度成线性关系长上下文不是免费的。上线前一定要先算清楚业务到底需要多长上下文别一上来就把128K拉满否则你会在并发能力上付出巨大代价。5.2 PCIe版本H20的通信瓶颈我有一台测试机器是PCIe版H20没有NVLink压测并发16时发现GPU利用率平均只有50%左右单卡之间忽高忽低总吞吐明显比NVLink版低一截。查了nvidia-smi topo -m之后确认8张卡跨了两个CPU socketPCIe switch之间通信带宽不够MoE模型频繁的allreduce通信直接被卡住。这个问题没有完美的软件解法只能尽量避免在PCIe版本上追求极端高并发或者用--tensor-parallel-size配合节点规划减少跨socket通信。5.3 量化导致输出质量明显下降团队里有人为了压显存在FP8基础上又做了INT4量化结果内部代码生成评测集上的通过率掉了将近6%。MoE模型对量化误差的累计效应比稠密模型严重专家路由的微小偏差可能改变最终的生成路径这种质量损失在高难度推理任务里体现得非常明显。如果我给你一个建议在显存真正不够之前别对R1做二次量化FP8已经是性价比最合适的档位。5.4 长上下文请求导致服务假死压测长输入8192以上时曾出现过某个请求长时间不返回后续请求也全部卡住的情况问了下是vLLM在处理长输入prefill时占用了大量算力block了其他decode请求。后来开启了--enable-chunked-prefill长请求的prefill被切成小块穿插执行服务整体的稳定性好多了TTFT也随之下滑了一些。如果你的业务有大量超长文档输入这个参数务必要开。5.5 重启服务后第一次请求奇慢vLLM每次重启后第一次请求都会慢得离谱因为模型权重要从磁盘重新加载到显存这个过程在H20上可能持续几分钟。我当时以为服务挂了查了很久才发现是权重加载导致的。解决方式是加一个健康检查和预热逻辑在服务启动后主动发一个短请求暖机然后再把它接入流量。运维上最好配合进程守护模型加载期间不要把服务当作健康状态。6. 如果你也要部署R1这几个经验提前拿走整个部署和压测过程走下来我最深的体会是H20跑671B模型真正决定成败的不是显卡算力而是显存规划和通信拓扑。显存算明白通信不拉胯vLLM配置得当这套系统的稳定性是能打的生产级水平。最后再分享几个我在实际运维中总结的落地要点监控不要只看GPU利用率重点盯TTFT和KV cache占用这两个指标才是推理服务用户体验和容量规划的核心。vLLM的--served-model-name参数很实用对外暴露一个短名内部实际加载671B权重这样前端配置不用跟着模型路径走。压测一定要分层先用小并发确认单卡性能再逐步加并发观察吞吐曲线拐点不要一上来就64并发打满否则出了问题很难定位是硬件、网络还是配置。如果业务允许在上游加一层nginx做超时控制和重试能把个别慢请求对全局的影响降到最低。这套方案不是唯一解但它是基于H20硬件特性、DeepSeek R1模型架构、vLLM框架能力三者匹配出来的务实组合。如果你也正打算把R1 671B部署在H20上希望这篇文章能帮你少走几步弯路把更多时间花在真正有价值的事情上。