ARTICLE DETAIL

资讯详情

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

大模型部署调优实战:延迟、吞吐与显存的铁三角平衡

大模型部署调优实战:延迟、吞吐与显存的铁三角平衡 1. 部署调优的底层逻辑为什么延迟、吞吐、显存是铁三角搞开源模型部署的人迟早都会撞上同一堵墙模型跑起来了但要么慢得没法用要么并发一上来就崩要么显存直接爆掉。这三个问题——延迟、吞吐、显存——从来不是孤立存在的它们构成一个互相拉扯的铁三角你动其中一个另外两个必然受影响。我见过太多人一上来就盯着某个参数猛调结果按下葫芦浮起瓢。比如为了降延迟把batch size压到1吞吐直接跌到谷底为了省显存把精度砍到int4结果输出质量崩了延迟反而因为反量化开销上去了。所以调优的第一步不是动手而是先搞清楚这三者之间的约束关系。延迟分两种首字延迟TTFTTime To First Token和每token延迟TPOTTime Per Output Token。前者决定用户等多久才看到第一个字后者决定输出速度。对话场景对TTFT敏感批量生成场景对TPOT更敏感。吞吐通常用tokens/s或requests/s衡量是系统级指标。显存则是硬约束决定了你能跑多大的模型、开多大的batch、留多少KV Cache空间。这三者的关系可以用一个简单的模型来理解显存 模型权重 KV Cache 激活值 框架开销。模型权重是固定的KV Cache随batch size和序列长度线性增长激活值跟batch size相关。当显存不够时你要么减batch要么减序列长度要么量化权重——每一种选择都会影响延迟和吞吐。很多人忽略的一点KV Cache的显存占用经常被低估。以LLaMA-7B为例FP16下每个token的KV Cache约0.5MB如果并发128个请求、每个请求2048 token光KV Cache就要128×2048×0.5MB≈128GB远超模型本身的14GB。这就是为什么高并发场景下显存总是先被KV Cache吃光。所以调优的核心思路是先确定显存预算再在预算内最大化吞吐最后针对场景优化延迟。顺序不能反。下面我会按这个逻辑把每个环节的实操细节拆开讲。2. 显存预算的精打细算从模型加载到KV Cache2.1 模型权重的显存占用怎么算模型权重的显存占用有一个简单公式参数量 × 每参数字节数。FP16是2字节int8是1字节int4是0.5字节。但实际占用会比理论值高一些因为还有embedding层、layer norm参数、以及框架的额外开销。以常见的7B模型为例精度理论显存实际显存含开销备注FP3228GB30GB基本没人这么部署FP1614GB15-16GB最常用的推理精度int87GB8-9GB需要量化校准int43.5GB5-6GB质量损失明显实测下来int4量化在6G显存上跑7B模型是可行的但前提是序列长度不能太长batch size只能开1。热词里提到的“6g显存”和“三进制bonsai27b”这类方案本质上都是通过极端量化把权重压到显存能装下的程度。2.2 KV Cache的显存计算与优化KV Cache的公式是2 × num_layers × num_heads × head_dim × seq_len × batch_size × dtype_bytes。简化后就是2 × hidden_size × num_layers × seq_len × batch_size × dtype_bytes。以LLaMA-2-7B为例hidden_size4096, num_layers32, FP16单token KV Cache 2 × 4096 × 32 × 2 bytes 512KB2048 token序列 1GBbatch size 8 8GB这就是为什么长上下文场景显存消耗特别快。优化KV Cache有几个方向PagedAttention把KV Cache分成固定大小的block按需分配减少碎片。vLLM的核心创新就是这个实测能提升2-4倍吞吐。KV Cache量化把KV Cache也量化到int8显存直接减半质量损失很小。现在主流推理框架都支持。滑动窗口注意力只保留最近N个token的KV Cache热词里的“滑动窗口滤波器延迟”就是这个思路的变体。Mistral系列模型就用了这个机制。GQA/MQA减少KV head数量。LLaMA-2-70B用了GQAKV Cache比同规模MHA模型小8倍。2.3 激活值和框架开销激活值在推理时通常不大但prefill阶段会临时占用较多显存。框架开销包括CUDA context、cuBLAS workspace等一般预留1-2GB比较稳妥。实操心得用nvidia-smi看到的显存占用往往比理论值高因为PyTorch有缓存分配器。真正要看的是torch.cuda.memory_allocated()和torch.cuda.max_memory_allocated()。调优时建议先跑一个空模型记录基线开销再逐步加载。3. 吞吐量最大化的实操路径3.1 Batch Size的甜点区怎么找Batch size是吞吐调优最直接的杠杆但不是越大越好。存在一个临界点超过之后吞吐不再增长延迟却急剧上升。找甜点区的方法固定序列长度从batch1开始逐步翻倍记录吞吐和延迟。通常会出现三个阶段线性增长区吞吐随batch近似线性增长GPU利用率不足饱和区吞吐增长放缓GPU接近满载过饱和区吞吐持平或下降延迟飙升甜点区通常在饱和区的起点。以A100 80G跑7B FP16为例序列长度512时batch32左右进入饱和吞吐约3000 tokens/s。3.2 Continuous Batching为什么能提升数倍吞吐传统静态batching要等一个batch里所有请求都完成才能处理下一批短请求被长请求拖累。Continuous Batching也叫in-flight batching在单个请求完成时立即插入新请求GPU几乎不空闲。实测数据静态batching下混合长短请求的吞吐约800 tokens/s换成continuous batching后同样硬件跑到2500 tokens/s提升3倍。vLLM、TensorRT-LLM、TGI都支持这个机制。3.3 并行策略的选择TP、PP还是DP策略适用场景优点缺点张量并行TP单机多卡模型装不下单卡延迟低通信开销大需要NVLink流水线并行PP多机多卡通信量小有气泡延迟高数据并行DP模型能装下单卡要提吞吐实现简单显存冗余单机8卡跑70B模型TP8是常见选择。如果模型能装下单卡比如7BDP比TP更划算因为TP的通信开销会拖累延迟。踩过的坑TP的通信开销在PCIe卡上非常明显。同样TP4NVLink下延迟增加15%PCIe下增加60%。如果机器没有NVLink优先考虑PP或DP。4. 延迟优化的关键手段4.1 首字延迟TTFT的构成与压缩TTFT 请求排队时间 prefill时间 首token采样时间。Prefill时间跟输入长度成正比跟模型大小成正比跟硬件算力成反比。压缩TTFT的手段Chunked Prefill把长输入切成小块跟decode请求交错执行避免长prefill阻塞其他请求。这是vLLM的默认行为。Prefix Caching如果多个请求有相同前缀比如system prompt缓存前缀的KV跳过重复计算。实测能降低50%以上的TTFT。投机解码用小模型草稿多个token大模型并行验证减少decode步数。对TTFT帮助不大但能显著降低TPOT。4.2 每token延迟TPOT的优化TPOT主要受限于内存带宽。每生成一个token都要把模型权重从显存读一遍。7B FP16模型是14GBA100的显存带宽是2TB/s理论最低TPOT 14GB / 2TB/s 7ms。实际因为kernel launch开销、采样开销等通常在15-25ms。优化方向量化权重int8把带宽需求减半TPOT接近减半。int4再减半但质量损失需要评估。Kernel融合把多个小kernel合并成一个大kernel减少launch开销。TensorRT-LLM和vLLM都做了大量融合。Flash Attention优化attention计算减少显存读写。现在基本是标配。4.3 调度策略对延迟的影响调度策略决定了请求的处理顺序。FCFS先来先服务简单但容易造成长请求阻塞短请求。优先级调度可以给短请求更高优先级降低平均延迟。vLLM支持--scheduling-policy参数可选fcfs和priority。实测在混合负载下priority策略能把P99延迟降低30%以上。5. 量化方案的取舍与实测对比5.1 主流量化方案对比方案精度显存节省质量损失适用场景FP16最高基准无质量优先int8 (bitsandbytes)高50%极小通用int8 (GPTQ)高50%小GPU推理int4 (GPTQ)中75%中等显存受限int4 (AWQ)中高75%较小显存受限GGUF Q4_K_M中75%中等CPU/混合AWQ在int4下质量损失比GPTQ小因为它是activation-aware的会保护重要权重通道。实测7B模型int4 AWQ在MMLU上只掉1-2个点GPTQ掉2-3个点。5.2 量化对延迟和吞吐的实际影响量化不仅省显存还能提吞吐降延迟因为内存带宽是瓶颈。实测数据A100 80G7B模型batch16序列512精度吞吐(tokens/s)TPOT(ms)显存(GB)FP1628001816int84200129int4550096int4的吞吐是FP16的近2倍TPOT减半。但要注意量化模型的prefill阶段可能因为反量化开销而变慢需要实测。注意事项量化不是免费的午餐。int4在数学推理、代码生成等任务上质量损失明显。建议在目标任务的验证集上实测不要只看perplexity。6. 常见问题与排查技巧实录6.1 显存溢出OOM的排查顺序OOM是最常见的问题排查顺序确认模型权重占用torch.cuda.memory_allocated()看实际占用检查KV Cache配置gpu_memory_utilization设太高会导致OOMvLLM默认0.9建议先设0.8检查max_model_len设太大导致KV Cache预留过多检查batch size动态batching下并发请求数决定实际batch检查框架开销CUDA context、cuBLAS workspace等6.2 吞吐上不去的常见原因GPU利用率低batch太小或者请求间隔太长CPU瓶颈tokenizer或调度器成为瓶颈top看CPU占用通信瓶颈TP场景下NVLink没跑满nvidia-smi topo -m看拓扑内存带宽瓶颈TPOT已经接近理论下限只能靠量化6.3 延迟毛刺的定位方法P99延迟远高于P50说明有毛刺。定位方法看调度日志vLLM会记录每个请求的排队时间、prefill时间、decode时间看GPU利用率曲线毛刺通常对应GPU空闲或满载看请求长度分布长请求会阻塞短请求独家技巧在vLLM里开--disable-log-requests会关掉请求日志但调优时一定要开着。日志里的prefill_time和decode_time是定位延迟问题的关键。7. 不同硬件配置下的调优策略7.1 单卡6G显存的极限部署6G显存跑7B模型必须int4量化且序列长度不能超过1024batch size只能1。推荐用llama.cpp的GGUF格式支持CPU offload把部分层放CPU。配置示例./llama-server -m model.Q4_K_M.gguf -ngl 20 -c 1024 -b 1-ngl 20表示20层放GPU其余放CPU。实测TPOT约50ms能用但不算快。7.2 单卡24G的均衡配置24G如3090/4090跑7B FP16很宽裕可以开较大batch。推荐vLLMpython -m vllm.entrypoints.openai.api_server \ --model meta-llama/Llama-2-7b-hf \ --gpu-memory-utilization 0.85 \ --max-model-len 4096 \ --max-num-seqs 64实测吞吐约2500 tokens/sTPOT约20ms。7.3 多卡80G的高吞吐配置8×A100 80G跑70B模型TP8。vLLM配置python -m vllm.entrypoints.openai.api_server \ --model meta-llama/Llama-2-70b-hf \ --tensor-parallel-size 8 \ --gpu-memory-utilization 0.9 \ --max-model-len 8192 \ --max-num-seqs 256实测吞吐约8000 tokens/sTPOT约30ms。8. 监控与持续调优的落地方法调优不是一次性的负载变了、模型换了、硬件升级了都需要重新调。建立监控体系很重要。关键指标TTFT P50/P99首字延迟TPOT P50/P99每token延迟吞吐tokens/sGPU利用率nvidia-smi dmon显存占用nvidia-smi或框架自带指标请求队列长度反映系统压力Prometheus Grafana是常见组合。vLLM自带Prometheus metrics直接暴露在/metrics端点。最后分享一个小技巧调优时先用固定长度的合成请求压测找到基线性能再用真实流量验证。合成请求可以用vllm.benchmarks里的脚本支持自定义输入输出长度分布。这样能把变量控制住避免真实流量的随机性干扰判断。我个人在实际操作中的体会是调优最忌讳的是同时改多个参数。每次只动一个记录前后数据才能知道哪个参数真正起作用。另外不要迷信网上的“最优配置”硬件、模型、负载都不一样别人的甜点区可能是你的过饱和区。老老实实做压测数据不会骗人。
返回列表