ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从GPU-Burn到DeepSeek推理压测:服务器验收实战指南

从GPU-Burn到DeepSeek推理压测:服务器验收实战指南 很多人拿到一台新 GPU 服务器第一件事就是跑 GPU-Burn跑 30 分钟不出错就认为机器“稳了”。但实际上能扛住 GPU-Burn 的机器到了跑大模型推理时照样可能崩——显存不够、带宽不够、温度墙降频、框架调度跟不上这些都是烤机测不出来的。所以我现在的验收习惯是拿到服务器先把硬件稳定性的底GPU-Burn测一遍再把 DeepSeek 这类真实大模型部署上去用模拟线上推理请求的方式做一轮完整的压力测试。这里的“用 DeepSeek 压测”有两层意思一是把 DeepSeek 开源权重作为测试负载真实地打在 GPU 服务上二是让 DeepSeek 帮你生成、优化压测脚本把压测这件事本身也 AI 化。这篇文章就以我在 8 卡 A 系列A100/A800 均可服务器上压 DeepSeek-R1-Distill-32B 的完整过程为例讲清楚从方案设计、环境部署、工具脚本到结果分析的每个环节以及我踩过的那些坑。无论你是刚买服务器要验收还是准备把推理服务上线这篇文章都能给你一套可以直接抄作业的压测流程。1. 压测不等于烤机先用 DeepSeek 想清楚要压什么1.1 烤机测试和推理压测根本不是一回事GPU-Burn 做的事情很简单让 CUDA 核心满载跑矩阵乘法观察机器在 100% 算力负载下能不能稳定工作。它验证的是芯片和显存的基础电气性能比如虚焊、坏显存、供电不稳、散热能不能压住极值功耗。这类测试很重要但它只能回答一个问题“这张卡有没有坏”。真实的大模型推理负载完全不是这个形状。LLM 推理分成 prefill 和 decode 两个阶段prefill 阶段要并行处理整个输入 promptGPU 算力消耗很高decode 阶段则是一个 token 一个 token 地蹦出来计算密度很低但每个 token 都要把模型权重从头到尾读一遍显存带宽需求极高。所以你会看到一个很反直觉的现象GPU SM 利用率只有 60%显存带宽却先跑满了吞吐死活上不去。这种瓶颈GPU-Burn 永远测不出来因为它根本不模拟“每生成一个 token 就读一遍几十 GB 权重”这种访问模式。我用一张简单的表总结这两个测试阶段的分工工具/方式主要验证点能发现的问题GPU-Burn纯 CUDA 算力、高频显存访问下的硬件稳定性核心损坏、显存故障、供电不稳、散热崩溃nvidia-smi 实时监控运行状态采集显存占用、功耗、温度、时钟频率变化DeepSeek 推理压测真实业务负载下的全链路表现显存带宽瓶颈、KV cache 容量、框架调度能力、并发上限、温度墙降频所以我的建议很明确先 GPU-Burn 排掉硬件暗病再上 DeepSeek 做推理压测。前者是“这机器能不能开机稳定负载”后者是“这机器能不能真的把大模型服务跑好”。1.2 一个 32B 模型负载下GPU 的短板可能不是算力举个具体例子。DeepSeek-R1-Distill-Qwen-32B 的 FP16 权重差不多 64GB单张 80GB 显存的卡勉强能放下但推理过程中还需要额外空间给 KV cache 和中间激活值实际能跑的并发很低。如果压成 4-bit 量化AWQ/GPTQ权重大概缩到 18~20GB单卡 24GB 就能跑但对性能的影响就不是简单的“权重变小了所以更快”。量化之后decode 阶段每生成一个 token 仍然要把这份权重从头到尾读一遍。比如一张 H800 的显存带宽约 3.35TB/s量化后权重约 20GB单路 decode 的速度理论上限大概是 3350 / 20 ≈ 167 tokens/s。注意这只是一个非常粗糙的上界真实压测中因为要同时读写 KV cache、做 attention 计算、走虚拟化驱动能跑到理论值的 60%~70% 已经算不错了。这个估算的意义在于如果压测时单路 decode 速度远低于这个数值你就要怀疑是不是权重加载路径出了问题或者框架调用了一些低效的 kernel又或者是多卡 tensor parallel 的通信开销吃掉了太多时间。这种结论单纯靠烤机给不了你。1.3 明确你的验收指标压测不能漫无目的地“使劲打”你得先定好这场测试要回答什么问题。我每次压测都会先把指标写下来吞吐量端到端每秒生成的 token 数代表系统整体处理能力也是压测报告里最核心的数字。TTFTTime To First Token首 token 延迟也就是用户发出请求之后多久能看到第一个字。在线业务对这项非常敏感。TPOTTime Per Output Token平均每个输出 token 的耗时体现 decode 一跳的速度也间接反映显存带宽是否被瓜分。GPU 利用率/显存占用/功耗/温度判断 GPU 是否真正被用满以及是否触发了降频。测试场景也要提前定好。你是单卡跑 7B 量化模型还是多卡 tensor parallel 跑 32B目标并发是 8 还是 128输入 prompt 是短问题还是几千字的长文这三个变量对结果影响巨大。脱离场景谈压测结论基本等于没有结论。2. 环境搭建把 DeepSeek 真正跑在 GPU 上2.1 模型选择和推理框架选型压测的第一步是让 DeepSeek 模型在 GPU 服务器上稳定跑起来。这里说的“DeepSeek”我建议直接用开源权重系列最省事的是 DeepSeek-R1-Distill 系列的量化版本。显存紧张选 7B/14B显存充裕选 32B如果手头是多张 80GB 大卡也可以考虑更大体量的 DeepSeek-V3 系列但那个对推理框架和 MoE 结构的支持要求高不少不太适合作为第一次压测的起点。推理框架的选择上我用过几个方案最推荐 vLLM框架高并发吞吐部署复杂度适合场景vLLM高中等生产推理、压力测试首选SGLang高中等某些场景吞吐更高但生态略少Ollama低-中低单卡本地体验、适合入门TensorRT-LLM高高对延迟有极致要求的大厂场景为什么是 vLLM因为它内置 PagedAttention 来管理 KV cache显存利用率比传统方案高不少连续批处理和调度策略在并发压力下也比较靠谱而且它提供了 OpenAI 兼容接口压测脚本可以直接打 HTTP 请求响应体里自带 usage 字段token 数统计非常方便。这些都是压测时省心省力的关键。2.2 vLLM 部署的启动参数到底该填什么假设我手上有 4 张 80GB 卡要跑 DeepSeek-R1-Distill-Qwen-32B 的 AWQ 量化版vLLM 的启动命令是这样的CUDA_VISIBLE_DEVICES0,1,2,3 python -m vllm.entrypoints.openai.api_server \ --model /data/models/DeepSeek-R1-Distill-Qwen-32B-AWQ \ --tensor-parallel-size 4 \ --gpu-memory-utilization 0.9 \ --max-model-len 8192 \ --port 8000这几个参数是压测前必须理解的--tensor-parallel-size 4把模型切分到 4 张卡上并行推理。每生成一个 token卡与卡之间都要做一次同步通信走 NVLink所以多卡压测的吞吐不是翻倍关系通信开销会吃掉一部分收益。--gpu-memory-utilization 0.9让 vLLM 最多占用每张卡 90% 显存。很多人图省事填 1.0但一旦 KV cache 在压测中增长很容易直接触顶 OOM。预留 10% 给 CUDA context、内存碎片和突发峰值是更稳的做法。--max-model-len 8192这个值决定了 KV cache 的预分配空间。设太大会把显存占死导致并发吞吐上不去设太小长文本请求直接报错。建议按你业务里 95% 请求的最大长度来设而不是一上来就拉满 32K。启动之后先用一小段 curl 确认服务可用curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: DeepSeek-R1-Distill-Qwen-32B-AWQ, messages: [{role: user, content: 11等于几}], max_tokens: 32 }注意model字段要和启动时的模型名一致vLLM 对模型名做严格匹配写错了会直接 404。2.3 顺手用 DeepSeek 帮你写压测脚本环境起来之后别急着写压测脚本。你可以直接把需求丢给已经在跑的 DeepSeek让它给你生成一段压测代码。比如我常这样问“用 Python aiohttp 写一个并发压测脚本发送 chat/completions 请求统计吞吐、平均延迟、P95 延迟。”它生成的代码通常能直接用但我会人工核一遍再上机器因为 AI 生成的代码偶尔会在请求体字段上犯低级错误需要肉眼确认。3. 压测工具与实测方案设计别一上来就拉满并发3.1 压测脚本为什么我不用 JMeter 而是用 aiohttpJMeter 是压力测试里非常常见的工具网上也有一堆 jmeter 压测 deepseek 的教程。它做短平快的 HTTP 接口压测没问题但压 LLM 服务有几个别扭的地方一是流式响应很难统计实际生成的 token 数二是请求延迟是整体值不好区分 prefill 和 decode三是线程模型偏重开几百个线程时本机反而先变成瓶颈。所以我更推荐用 Python 的 aiohttp 写脚本配合 vLLM 接口的 usage 字段直接取 token 数吞吐统计天然好做。下面这个脚本是我压测时的基本模板import asyncio import aiohttp import time import statistics ENDPOINT http://localhost:8000/v1/chat/completions MODEL DeepSeek-R1-Distill-Qwen-32B-AWQ async def one_request(session): payload { model: MODEL, messages: [{role: user, content: 写一篇包含三个要点的关于云计算的文章每个要点不少于50字。}], max_tokens: 512, temperature: 0.7 } start time.perf_counter() async with session.post(ENDPOINT, jsonpayload) as resp: data await resp.json() latency time.perf_counter() - start output_tokens data.get(usage, {}).get(completion_tokens, 0) return latency, output_tokens async def main(concurrency, total_requests): sem asyncio.Semaphore(concurrency) async with aiohttp.ClientSession() as session: async def worker(): async with sem: return await one_request(session) results await asyncio.gather(*[worker() for _ in range(total_requests)]) lats [r[0] for r in results] toks [r[1] for r in results] total_time max(lats) total_tokens sum(toks) print(f并发: {concurrency}, 总请求: {total_requests}, 总耗时: {total_time:.2f}s) print(f平均吞吐: {total_tokens / total_time:.2f} tokens/s) print(f平均延迟: {statistics.mean(lats):.2f}s, P95: {sorted(lats)[int(len(lats) * 0.95) - 1]:.2f}s) if __name__ __main__: for c in [1, 4, 8, 16, 32, 64]: asyncio.run(main(c, 100))这个脚本里的并发数列表就是核心设计从 1 开始翻倍往上加。每个并发梯度跑 100 个请求如果某个梯度出现大面积超时或报错立刻停下来不要继续加码。3.2 梯度加压从 1 路并发拉到把 GPU 打爆我见过不少人压测时懒得做梯度直接用 JMeter 开 200 个线程上去压结果服务 10 秒就被打崩然后慌慌张张到处查日志。这种“暴力压测”查不出来什么因为系统失败的瞬间你根本分不清是显存爆了、CPU 调度不过来、还是模型推理框架队列满了。正确的做法是梯度加压先用 1 并发跑 100 个请求拿到基准延迟和单路吞吐。并发依次按 4、8、16、32、64 递增每个梯度跑 5 分钟以上。每 30 秒记录一次 nvidia-smi 的输出把 GPU 利用率、显存、温度、功耗留档。当 GPU 利用率稳定在 95% 以上或者出现明显错误/超时立刻停手。每个梯度跑 5 分钟不是强迫症。短时间压测根本暴露不了温度墙问题显卡从不热到撞墙通常需要 10~20 分钟KV cache 是否能被框架正确回收也需要时间验证。5 分钟是最低可接受长度我压正式验收通常每个梯度跑 15 分钟。3.3 压测期间监控命令眼睛要盯在 GPU 上压测脚本在跑眼睛不能闲着。我把常用监控命令贴在下面nvidia-smi dmon -s pucvmet -d 1这条命令每秒输出一次 GPU 利用率、显存利用、温度、功耗、SM 时钟等关键信息。列非常多但压测时我最关注三列GPU 利用率GPU%、显存利用mem%、温度temp。想看更精细的状态可以用nvidia-smi --query-gpuindex,utilization.gpu,utilization.memory,memory.used,memory.total,temperature.gpu,power.draw,pcie.link.gen.current,pcie.link.width.current --formatcsv -l 1多卡压测时还要额外观察 PCIe/NVLink 链路状态。如果卡间走的是 PCIe 而不是 NVLinktensor parallel 的通信开销会大很多吞吐可能直接掉三成这个从监控里就能看趋势。3.4 压测前先用 gpu-burn 抽检硬件正式跑 DeepSeek 推理压测之前我习惯先跑一轮 GPU-Burn尤其是刚上架的新机器。编译很简单git clone https://github.com/wilicc/gpu-burn cd gpu-burn make ./gpu_burn 18001800 是秒数也就是 30 分钟。执行完后看输出如果出现 Failed说明核心或显存大概率有问题这种机器没必要继续压 DeepSeek直接找供应商换。千万别图省事跳过这一步否则你压测时看到吞吐异常会分不清是模型配置问题还是硬件暗病。4. 压测结果怎么看数据不会骗你但前提是你得会读表4.1 一组真实数据示例吞吐量和延迟的拐点下面这组数据是我在一台 8 卡 A800 服务器上压 DeepSeek-R1-Distill-Qwen-32B-AWQ4 卡 tensor parallel时记录的大致结果请求输入约 200 token输出上限 512 token。并发数平均吞吐 (tokens/s)TTFT (s)TPOT (s/token)GPU 利用率 (%)显存占用 (GB/卡)118.50.420.0451824.1471.30.580.0465226.88132.60.810.0487831.216148.41.560.0969235.932151.24.230.2389536.4注意看这几个变化的节奏。并发从 8 升到 16吞吐只涨了 12%132.6 → 148.4但 TTFT 从 0.81 翻倍到 1.56TPOT 也从 0.048 涨到 0.096等于 decode 速度直接慢了一倍。这说明系统已经进入“排队区”所有请求都在抢 GPU 的显存带宽和计算资源。到并发 32 时吞吐基本封顶在 151TTFT 到了 4.23 秒用户在线上就是“发出去要等 4 秒才看到第一个字”的体验基本不可用。注意这组数值基于我当时的模型、请求长度和框架版本换一台机器一定能复现这个趋势但具体数字不会完全一致。真正通用的是规律吞吐永远不会线性增长TPOT 一旦开始明显变差说明 decode 阶段的资源已经被瓜分完了性能拐点就到了。4.2 从结果反推瓶颈算力、显存带宽还是框架调度同一份压测数据不同的人读出来的东西完全不同。我常用的判断逻辑是GPU 利用率已经 90%但吞吐不再上涨说明算力或将满或者显存带宽是硬瓶颈。此时继续加并发没意义要优化模型本身比如量化、剪枝或者换更高带宽的卡。并发上去之后 TTFT 暴涨、TPOT 还算正常问题出在 prefill 阶段的计算争抢。可以看 vLLM 有没有开启 chunked prefill--enable-chunked-prefill这个参数对长 prompt 并发场景改善非常明显。GPU 利用率并不高但请求已经超时或者连接被重置大概率不是 GPU 不行而是框架的调度器、入口代理或者 CPU 环节先顶不住了。这时候要去看 vLLM 进程的 CPU 占用和网络连接状态。压测不是看个吞吐就完事儿这层反向推导才是最有价值的部分。4.3 多卡场景下额外要看的指标单卡压测看利用率就够了多卡服务要额外关注卡间通信。我用 tensor parallel 跑 4 卡时每个 token 生成都要做一次 allreduce这个同步操作完全依赖 NVLink。如果卡间走的是 PCIe吞吐可能比 NVLink 低 20%~40%这也是为什么很多推理框架文档里都强调“TP 优先用 NVLink 互联”。另一个隐性指标是 CPU 负载。vLLM 的调度器是典型的 CPU 密集任务请求量一大CPU 单核可能先被打满GPU 反而闲下来。监控脚本里加上top或mpstat -P ALL 1能及时发现这类非 GPU 瓶颈。5. 实测踩坑记录与调优清单5.1 显存 OOM 的三种典型姿势压测中最常见的故障就是 OOM我至少踩过三种套路第一种max_model_len设太大。我曾在 24GB 单卡上跑 7B 模型把 max_model_len 设成 32K启动时模型就占了 15GBKV cache 预分配又占掉七八 GB结果并发压测一上来就爆。后来改成 8192问题才解决。这事的教训是KV cache 是按最大长度预分配的别为永远不会出现的超长文本买单。第二种并发开太多导致 KV cache 撑爆。vLLM 虽然有 PagedAttention但每个请求都占独立 KV cache 块并发翻倍显存消耗也跟着翻倍。OOM 时 vLLM 日志里通常会明确写出“out of memory”下面跟着每个 request 的 KV cache 大小一看就知道是谁吃掉的。第三种prefill 峰值显存冲高。长 prompt 请求一次性做 attention 计算时激活值会短暂冲高如果平时显存占用 85%一个长文本请求就可能直接顶破上限。这类问题在只压短文本时不会暴露所以压测时我每天都会混进几条 2000~4000 token 的长输入。5.2 温度墙降频压测 20 分钟后性能下降有一次压测让我印象很深前 10 分钟吞吐稳定在 148 tokens/s20 分钟后掉到 121并且还在往下跌。第一时间怀疑显存泄漏结果看 nvidia-smi显存占用没变化但温度已经来到 85℃GPU 核心时钟从 1980MHz 掉到了 1500MHz——这是撞到温度墙被强制降频了。这种问题在烤机阶段也不会暴露因为 GPU-Burn 从一开始就跑满载你会很自然地接受“GPU 就是应该这么热”。但推理压测是动态负载温度墙降频往往发生在你误以为“系统已经稳定”的阶段。压测时长太短真的发现不了。解决思路也很直接检查机房风道和服务器进风口温度用nvidia-smi -pl 350限制功耗上限让温度不要冲到太高如果是新机器还要确认散热器贴装没问题。5.3 请求超时与连接被重置不全是 GPU 的锅压到高并发时脚本里经常冒出一堆 timeout 和连接重置。新手第一反应就是“GPU 不行”实际上很多时候是三层因素叠加一是 vLLM 的调度器在极端并发下先把 CPU 吃满请求排队时间过长二是入口代理的配置有问题比如 nginx 的 keepalive 超时太短三是你压测脚本里的 timeout 设置不合理LLM 请求本来就不是秒回几秒的超时必然误判。我的经验是先分三步排查看 GPU 利用率如果没打满GPU 不是第一嫌疑人看 vLLM 日志里的排队请求数看压测机自身的 CPU 和连接数。很多时候把超时时间从 5 秒放宽到 60 秒错误率就清零了。5.4 最终调优清单跑完一轮完整的 DeepSeek 推理压测之后我会把调优项整理成一张清单每个项目都有明确的实施路径vLLM 的 continuous batching 默认开启不用额外动但并发调度参数值得研究。如果业务长 prompt 偏多开启--enable-chunked-prefill把 prefill 阶段切成小块减小长请求对 TTFT 的冲击。模型支持 FP8 的优先用 FP8相比 AWQ 在部分框架里有更快的 kernel论吞吐实测通常高 5%~10%。多卡场景下 tensor parallel 一定要走 NVLink避免 PCIe 直连。压测结束后回头看一眼dmesg如果有 Xid 错误说明硬件有问题需要立刻处理不要带着隐患上线。压测这件事说穿了就是用真实负载把系统的短板暴露出来。DeepSeek 模型负载恰好涵盖了算力、显存带宽、KV cache 容量、框架调度、卡间通信、散热和电源这七层比任何单一的烤机工具都更能还原线上情况。我个人现在的体会是压测不是机器到货时验一次的临时动作而是每次模型版本更新、每次推理框架升级、每次并发策略调整后都要重复做的基础操作。把这套流程固化下来服务器上线之后能少睡很多安稳觉的副作用。
返回列表