DeepSeek本地部署:显存规划、量化与多卡避坑指南

发布时间:2026/10/29 21:58:57
DeepSeek本地部署:显存规划、量化与多卡避坑指南

简介:面向希望在本地私有化运行DeepSeek的开发者与企业运维人员,这份PDF文档围绕硬件选型、系统环境搭建、依赖配置和性能优化给出分场景部署参考,兼顾个人学习测试与企业生产落地。内容按模型规模划分基础开发与生产级集群,说明7B以下和32B以上模型在处理器、显卡显存、内存、存储和网络方面的配置建议,并覆盖Ubuntu与Windows环境准备、CUDA与cuDNN安装验证、PyTorch及FlashAttention编译、分布式训练参数设置,以及4-bit量化、分级卸载等显存优化思路。文中还整理实时监控、指标采集和性能分析方式,便于部署后观察推理延迟、吞吐量与资源占用。资源包共1个PDF文件,约274KB,内容紧凑,适合作为部署前的环境核对清单和参数查阅手册。已有2219人学习,个人开发者可据此判断本地显卡与内存是否够用,企业团队也能参考集群节点、网络和存储规划,降低反复试错成本。

1. 显存账本:为什么"装得下模型"和"跑得起来"差着一倍显存

两张同样 24GB 的卡,一张把 7B 模型跑得顺顺当当,另一张连权重都加载不完——差别往往不在卡,而在序列长度和并发数。DeepSeek 本地部署的硬件要求与环境配置,真正难的地方从来不是"该买哪张显卡",而是搞清楚显存到底被谁吃掉了。很多人按"每十亿参数 1.2GB"算出 7B 只要 8.4GB,觉得 12GB 卡绰绰有余,结果一加载就报显存溢出。权重只是第一笔账,KV Cache 和中间激活才是后面那只手,而它们随序列长度线性甚至平方增长。搞懂这三笔账怎么分开算,你才能在选卡、量化、开并发之前就知道天花板在哪,而不是靠试。

2. 硬件选型:速算系数为什么不能拿来做容量规划

2.1 权重、KV Cache、激活值三笔账怎么分开算

容量规划里最容易翻车的一步,是把"权重显存"当成"运行显存"。权重是一次性常驻的固定开销,加载完就不再变;KV Cache 与并发数和上下文长度成正比,是运行期动态增长的;激活值和临时张量则是每个 forward 的峰值开销。三者性质完全不同,混在一起估就会得到"跑得起来但一并发就崩"的结论。

按字节严格算,FP16/BF16 下每个参数占 2 字节,7B 模型光权重就是 14GB。文档里"每十亿参数 1.2GB"这个系数,是把权重、部分缓存和框架开销揉在一起的粗算值,适合判断"这张卡能不能点亮",不适合做容量规划——它天然偏乐观。下面这段代码把三笔账拆开:

def vram_budget(params_b, bytes_per_param, layers, kv_heads, head_dim, seq_len, batch, vocab, dtype_bytes=2): # 1) 权重:常驻,不随并发变化 weights = params_b * 1e9 * bytes_per_param / 1024**3 # 2) KV Cache:2 份(K 和 V)× 层数 × KV头数 × 头维度 × 序列 × 并发 # 注意这里用的是 kv_heads 不是注意力头总数,GQA/MQA 下两者差好几倍 kv = 2 * layers * kv_heads * head_dim * seq_len * batch * dtype_bytes / 1024**3 # 3) 输出 logits:只在训练/打分时需要全序列,纯生成只算最后一个位置 logits = seq_len * batch * vocab * dtype_bytes / 1024**3 # 4) 框架固定开销:CUDA context、cuBLAS workspace、碎片,留 1~2GB overhead = 1.5 return weights, kv, logits, overhead w, kv, lg, ov = vram_budget(7, 2, layers=32, kv_heads=32, head_dim=128, seq_len=8192, batch=1, vocab=150000) print(f"权重 {w:.1f}G / KV {kv:.1f}G / logits {lg:.1f}G / 开销 {ov}G")

代入一组典型的 7B 级结构参数(32 层、32 个 KV 头、head_dim=128、8K 上下文、单并发),权重 14GB、KV Cache 约 4GB、固定开销 1.5GB,合计接近 20GB。这时 24GB 卡还能喘气,但把 batch 提到 4,KV 直接变成 16GB,总量冲到 31GB 以上,立刻溢出。同一个模型、同一张卡,只因为并发数不同,结论就从"能跑"变成"跑不了"——这就是为什么容量规划必须按峰值并发算,而不是按单条请求算。

再补一个常被忽略的点:KV Cache 的大小取决于 KV 头数,不是注意力头总数。采用分组查询注意力或把 KV 压成低秩潜向量的结构,长上下文下的缓存能小一个数量级,这也是为什么同样标称参数量的模型,在实际部署时长上下文表现差别很大。选型时看参数量只是第一步,看结构才决定你的显存能撑多长的上下文。

2.2 张量并行为什么被互联带宽而不是算力卡住

单卡装不下时,第一反应是切到多卡。切法有两种:张量并行把每一层的矩阵按列或按行切开,每张卡算一部分;流水并行把不同的层放到不同卡上,前后接力。前者对带宽极其敏感,后者对带宽相对宽容,原因在于通信频率。

张量并行在每一层的前向和反向里都要做一次 all-reduce,把各卡的局部结果汇总。层数越多,通信次数越多,而且这些通信和计算是串行依赖的——算完才能通信,通信完才能进下一层。这意味着 GPU 有相当一部分时间在等数据,而不是在算。带宽不够时,加卡不但不提速,反而更慢。

对照一下带宽量级就明白了:

| 互联方式 | 单向带宽量级 | 延迟量级 | 适合承载 | | 单卡 HBM | 1~3 TB/s | 百纳秒 | 权重、KV Cache | | 机内 NVLink | 300~450 GB/s | 微秒 | 张量并行 all-reduce | | PCIe 4.0 x16 | 25~30 GB/s | 十微秒 | 参数卸载、主机拷贝 | | 200Gb 高速网络 | 20~25 GB/s | 数十微秒 | 流水并行、梯度同步 |

机内高速互联和 PCIe 差了一个数量级,和跨机网络差了一个半数量级。所以工程上的通行做法是:张量并行只在单机 NVLink 域内做,组大小不超过一台机器的卡数;跨机用流水并行或数据并行,因为它们的通信频率低得多。文档里 4×80GB 的计算节点配 NVLink 互联、节点间配 200Gb 网络,正是这个约束的体现——不是堆料,是被通信模型逼出来的。

还有一层:decode 阶段本身就是访存瓶颈。每生成一个 token,都要把全部权重从显存读一遍,所以理论出词速度约等于显存带宽除以权重大小。24GB 级消费卡约 900GB/s 带宽,跑 14GB 的 FP16 权重,上限七十 token/s 左右;数据中心卡带宽翻倍,上限也跟着翻倍。这解释了为什么"算力强"的卡在推理场景不一定快——瓶颈在搬数据,不在算数。

2.3 从单卡到四卡:分档配置背后的物理依据

把上面的账套进具体档位,选型就不再是玄学。

7B 以下、个人开发测试:FP16 权重 14GB,加上 KV 和开销,24GB 卡是舒适线,16GB 卡只能跑短上下文或必须量化。这一档的瓶颈是显存容量而非带宽,所以优先保容量。

32B 以上、生产服务:FP16 权重 64GB,单卡放不下,必须切分。四张 80GB 卡做张量并行,每卡分到 16GB 权重,剩下的空间留给 KV Cache 和并发批次。这一档的瓶颈转移到互联带宽和调度效率,卡数与拓扑的匹配比单卡规格更重要。

中间还有一个常被跳过的档:13B~14B。权重约 28GB,24GB 卡放不下,48GB 卡放得下但没余量,实际最划算的做法是 4-bit 量化塞进单张 24GB 卡,或者两张卡做张量并行。

| 参数档 | FP16 权重 | 4-bit 权重 | 显存底线 | 现实建议 | | 7B 级 | ~14 GB | ~3.6 GB | 16 GB | 单张 24GB 卡,FP16 直跑 | | 13B 级 | ~28 GB | ~7 GB | 24 GB | 单卡 4-bit,或双卡张量并行 | | 32B 级 | ~64 GB | ~16 GB | 80 GB | 4×80GB 张量并行,或单卡 4-bit 降级跑 | | 70B 级以上 | ~140 GB | ~35 GB | 多机 | 张量并行 + 流水并行混合 |

注意最右列"降级跑"的含义:4-bit 塞进单卡能加载,但吞吐和首 token 延迟都会明显劣化,只适合离线批处理或低频调用。生产环境追求稳定延迟,宁可多卡,不要靠量化硬扛。

3. 软件栈咬合:CUDA、PyTorch、FlashAttention 三层的版本锁

3.1 驱动、CUDA、PyTorch 的版本链为什么一环错全盘废

环境配置里最折磨人的不是装不上,而是"装上了但用不了"。原因在于这条链上有三个不同的东西都叫 CUDA:显卡驱动支持到的 CUDA 版本上限、系统里安装的 CUDA Toolkit、以及 PyTorch 二进制包内置的 CUDA 运行时。三者版本不必相同,但必须满足约束关系,任何一环错位都会表现为同一种症状——算子报错或直接回落到 CPU。

按顺序查,一步都别跳:

# 1. 驱动能支持到的 CUDA 版本上限(右上角那行,不是已安装版本) nvidia-smi # 2. 系统里真正装的 toolkit,只影响需要现场编译的算子 nvcc -V # 3. PyTorch 自带的运行时版本,决定绝大多数算子能不能跑 python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())" # 4. 显卡算力等级,决定编译算子时该填什么架构 python -c "import torch; print(torch.cuda.get_device_capability())"

读法很直接:第一步给出天花板,第三步的运行时版本不能超过它;第二步的 toolkit 版本只要不低于第三步就行,因为运行时是 PyTorch 自己带的,和系统 toolkit 是两套库,同时存在不冲突。真正被系统 toolkit 卡住的只有需要现场编译的扩展——注意力加速库、推理框架的 CUDA 算子、分布式训练的部分组件,这些在编译时链接的是系统 toolkit。

第四步决定的架构参数是另一个高频翻车点。编译时如果没指定目标架构,构建系统会尝试覆盖一长串架构,编译时间翻好几倍,还极容易在并行编译时把内存打满。指定了但填错,编出来的二进制里没有对应显卡的 kernel,导入时直接报"no kernel image is available"。消费卡和数据中心卡的算力等级不同,这个值必须按实际硬件填。

3.2 FlashAttention-2 省的不是算力,是 HBM 读写

很多人以为注意力加速是把计算量降下来了,其实标准注意力的浮点运算次数一点没少,省的是显存读写和中间结果的物化。

标准实现里,注意力分数矩阵要先算出来、写回显存、再读出来做 softmax、再写回、再读出来乘 V。序列长度翻倍,这个矩阵的元素数翻四倍,显存占用和读写量都按平方涨。8K 上下文、多头的情况下,这个中间矩阵能吃掉几十 GB。

分块加在线 softmax 的思路是把 Q 切成小块,逐块和 K 计算,在寄存器或共享内存里维护一个"当前最大值"和"当前归一化因子",边算边修正。这样完整的分数矩阵从头到尾不需要被完整物化,显存占用从平方级降到线性级,跨显存的数据搬运量也降了一个数量级。数学上等价,工程上完全不同。

# 只给目标卡编译,别用默认全架构 export TORCH_CUDA_ARCH_LIST="8.0" # 80GB 数据中心卡=8.0,24GB 消费卡=8.6,新卡=8.9 export MAX_JOBS=4 # 限制并行编译进程,防止内存被打满后 OOM pip install flash-attn==2.3.3 --no-build-isolation
import torch from flash_attn import flash_attn_func # 形状约定是 [batch, seqlen, nheads, headdim],和多数框架的 [batch, nheads, seq, dim] 不同 q = torch.randn(1, 4096, 8, 128, dtype=torch.bfloat16, device="cuda") out = flash_attn_func(q, q, q, causal=True) print(out.shape, torch.cuda.max_memory_allocated() / 1024**3, "GiB")

这段验证代码的重点不是看输出对不对,而是看max_memory_allocated的量级。同样的形状用标准注意力实现,峰值会高出数倍。写代码时最容易踩的是形状约定——这个接口用序列在前的布局,而多数框架的张量是头在前的,直接传进去不会报错,只会算出莫名其妙的结果,属于那种不炸但明显不对的坑。

3.3 系统层的几个"小参数":swappiness、锁页内存与文件句柄

软件栈对齐之后,还有一批藏在系统层、影响巨大的参数。它们不改变功能,只影响稳定性——表现出来就是"跑得好好的,突然卡几十秒"。

最典型的是交换分区策略。模型权重是几百 GB 的匿名内存块,一旦内核判断内存紧张,会把这些页换到磁盘上。下一次前向传播访问到被换出的页,触发缺页中断,从 NVMe 读回来,单次卡顿能到秒级。把交换倾向调到很低,或者干脆在推理节点上关掉交换分区,就能消除这类抖动。

# 尽量不把进程内存换到磁盘,模型权重被换出后回读是秒级卡顿 sudo sysctl -w vm.swappiness=10 # 关闭 NUMA 本地内存回收,避免跨节点访问带来的额外延迟 sudo sysctl -w vm.zone_reclaim_mode=0 # 高并发下文件句柄耗尽会静默丢连接,排查时很难联想到 ulimit -n 1048576 # 固定 CPU 频率,避免调频策略导致的首 token 延迟抖动 sudo cpupower frequency-set -g performance

配合主机到设备的数据传输,还需要锁页内存:普通内存页可能被换出,传输时驱动必须先做一次页锁定,等于多一次拷贝;锁页内存直接 DMA,带宽能高出三到五成。推理框架通常会自动管理这部分,但如果自己做批量预处理,把缓冲区显式锁页是有意义的。

vm.swappiness=10这个值值得单独说一句:它不是一个"越小越好"的参数,设成 0 在部分内核版本上会导致内存回收行为异常,10 是一个折中——平时几乎不换出,内存真的紧张时还有退路。生产节点上更彻底的做法是直接不挂交换分区,把内存规划做到位。

4. 量化与卸载:nf4 和分层 offload 的代价结构

4.1 nf4 为什么在 4-bit 里几乎不掉点

量化最朴素的做法是等距取点:把权重范围线性切成 16 份。问题在于神经网络权重近似正态分布,绝大多数值集中在零附近,等距取点会让这些密集区域的量化误差特别大,而尾部的极端值又浪费了大部分码点。

nf4 换了个思路:按分位数取点。把 16 个量化点放在正态分布的等概率分位上,密集区码点密、稀疏区码点疏,量化误差在期望意义上最小。同时它按块计算缩放因子——每 64 个参数共享一个 absmax,而不是整个张量共用一个。这样即使某个通道存在极端值,也只影响所在的块,不会把其他块的精度拖下水。

双重量化再进一步:那些 absmax 常数本身也要占空间(每 64 个参数一个 32 位浮点,折合每参数 0.5 bit),把它们再按 256 个一组做一次 8 位量化,每参数开销降到约 0.127 bit。合起来每参数约 4.13 bit,比裸 4 bit 只多一点。

| 精度 | 每参数位宽 | 7B 权重显存 | 相对精度损失 | | FP16/BF16 | 16 | ~14 GB | 基线 | | INT8 | 8 | ~7 GB | 很小 | | NF4(含双重量化) | ~4.13 | ~3.6 GB | 可接受 | | NF4 + 分层卸载 | 视分层而定 | 可压到 2 GB 以下 | 明显,且延迟上升 |

这张表的关键信息不在第三列,而在第四列:显存省下来的部分,是用精度或延迟换的。而延迟的劣化往往比精度更致命,因为它直接决定服务能不能达标。

4.2 compute_dtype 与 device_map 的隐性开销

量化配置里有几个参数,选错不会报错,只会让结果变得"不太对"或者慢得莫名其妙。

from transformers import BitsAndBytesConfig, AutoModelForCausalLM import torch bnb = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", # 按正态分位数取点,不是等距切分 bnb_4bit_use_double_quant=True, # 对 absmax 常数再量化,省约 0.37 bit/参数 bnb_4bit_compute_dtype=torch.bfloat16, # 反量化后的计算精度,别用 fp16 ) model = AutoModelForCausalLM.from_pretrained( "/opt/deepseek/models/deepseek-32b", # 本地权重目录 quantization_config=bnb, device_map="auto", # 放不下的层会被静默甩到 CPU low_cpu_mem_usage=True, # 用 meta device 建空壳,避免 CPU 端出现两份权重 )

第一个要盯的是计算精度。4-bit 权重在参与矩阵乘之前会反量化回 16 位,这个 16 位用什么类型很关键。fp16 的指数位只有 5 位,动态范围窄,遇到大数值或累加溢出就会变成 inf 或 nan,表现出来是输出重复、乱码、突然开始复读。bf16 的指数位和 fp32 一样宽,代价是尾数精度低一些,但对推理来说几乎无感,属于默认选择。

第二个要盯的是设备映射。device_map="auto"做的事是按显存余量把层依次分配到显卡,放不下的甩到主机内存。它不会告诉你"有一半年在 CPU 上",只会让推理速度掉到原来的几分之一——因为每生成一个 token,那些放在主机内存里的层都要通过 PCIe 搬进显存一次。看到延迟是预期的三倍以上,先检查有多少层被分到了 CPU。

第三个是低内存加载。默认加载路径会先在主机内存里完整建一份权重,再往显存搬,等于峰值时内存里存在两份。对大模型来说这一步本身就能把主机内存打爆。低内存加载先用空壳占位,再按分片流式填入,峰值只多一个分片的量。

还有一个隐性开销容易被忽略:量化通常只作用于线性层权重,嵌入层和输出头往往保留原精度,加上量化常数、缩放因子、未量化的部分,实际显存占用会比"参数量 × 位宽"算出来的高 10%~20%。按理论值做容量规划,很容易卡在最后几百兆上。

4.3 参数卸载到 NVMe:省下的显存用什么换的

显存不够时,最直接的办法是把放不下的部分挪到下一级存储。这条路能走通,但代价必须提前算清楚。

accelerate launch \ --num_processes 4 \ --mixed_precision bf16 \ --gradient_accumulation_steps 8 \ train.py # 参数与优化器的卸载目标写在配置文件里,命令行不再重复指定
distributed_type: MULTI_GPU num_processes: 4 mixed_precision: bf16 deepspeed_config: offload_param: device: nvme # 参数卸载到本地固态盘 nvme_path: /opt/deepseek/offload offload_optimizer: device: cpu # 优化器状态留在主机内存

这套配置是训练场景的典型写法:模型状态、优化器状态、梯度三份东西里,优化器状态占比最大(Adam 类优化器每参数要存两个动量,FP32 下就是 8 字节),把它挪到主机内存能省下大头;参数本身挪到固态盘,进一步压显存。

代价看带宽表就一目了然:

| 存储层级 | 带宽量级 | 容量 | 适合放什么 | | HBM 显存 | 1~3 TB/s | 24~80 GB/卡 | 权重、KV Cache、激活 | | 主机内存 | 50~100 GB/s | 数百 GB | 放不下的层、优化器状态 | | 本地固态盘 | 3~7 GB/s | TB 级 | 参数卸载、检查点 | | 网络存储 | 数百 MB~GB/s | PB 级 | 模型仓库、日志归档 |

算一笔账:32B 模型 FP16 权重 64GB,卡上只有 24GB,意味着一轮前向要把 40GB 从主机侧搬进显存。按 PCIe 实际可用 25GB/s 算,光搬运就要 1.6 秒——每生成一个 token 都付这个代价,等于不可用。所以卸载的正确定位是"让本来跑不起来的配置能跑起来做验证或离线批处理",不是"让大模型在小卡上跑得快"。生产环境要的是稳定延迟,卸载换来的容量在这种场景下不值。

推理场景下还有个更划算的替代方案:把并发数压到 1、把上下文截短、用 4-bit 量化,通常比卸载更能保住延迟。卸载是最后手段,不是第一选择。

5. 避坑与排查:五类翻车现场的现象、成因与处置

5.1 显存类:报错溢出但监控还有空余

现象:程序抛出显存不足,但用显卡监控命令看,显存还有好几 GB 空闲;重跑一次有时成功有时失败。

原因:框架的显存分配器会按块缓存已申请的空间,反复申请释放不同大小的张量后,缓存里全是碎片,凑不出一块连续的大块,即便总空闲量够。另一个常见原因是上一次运行的进程没完全退出,残留的上下文占着显存,监控里看到的是被占用的部分。第三个原因是显卡本身的预留开销——显存纠错、驱动上下文、通信缓冲区都会吃掉几百兆到一 GB。

解决:打开可扩展段分配,让分配器把碎片拼起来;同时限制单次分配的粒度上限;上线前先确认没有残留进程。容量规划时按可用显存的 85%~90% 来算,剩下的留给框架和碎片,比事后排查省事得多。

# 允许分配器使用可扩展段,缓解碎片导致的假性溢出 export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True

5.2 显存类:加载到一半同时爆显存和内存

现象:模型加载到 60%~80% 时,显存和主机内存同时告急,进程被系统杀掉,日志里连异常都没有。

原因:默认加载路径会先在主机内存完整构建一份权重,再按设备映射往显存搬。对大模型来说,峰值时主机内存里存在完整的一份,显存里又有一份正在搬的,两边同时顶到天花板。

解决:启用低内存加载,用空壳占位再按分片流式填充,峰值只多一个分片;同时显式给设备映射,别让自动分配把层甩到 CPU 上还以为是纯显卡推理。

5.3 编译类:加速库装完一导入就崩

现象:安装过程一切正常,import时报找不到可用内核,或者直接段错误。

原因:编译时指定的目标架构和实际显卡的算力等级不匹配,编出来的二进制里没有对应硬件的内核;另一个高频原因是并行编译进程数开太大,编译到一半内存被打满,产物是残缺的,但安装流程照样报成功。

解决:先用一行代码查出显卡算力等级,把它填进架构环境变量再重编;把并行编译进程数压到 4 以内;能用预编译包就别自己编,编译环境和运行环境不一致是这类问题的温床。

5.4 通信类:四张卡比一张卡还慢

现象:单卡跑得好好的,扩到四卡张量并行后,吞吐不升反降,显卡利用率长期趴在三成以下。

原因:张量并行的每层都要做一次全局规约,通信和计算是串行依赖。如果这几张卡挂在 PCIe 上,或者跨了 CPU 插槽导致跨节点访问,带宽比机内高速互联低一个数量级,通信时间远超计算时间。另一种情况是通信库没有走点对点直连,数据绕了一圈主机内存。

解决:先看拓扑,确认参与张量并行的卡是否在同一个高速互联域内;不在就改成跨机流水并行;在的话检查通信库的点对点开关和环境变量,并把进程绑到对应 NUMA 节点上。

# 先看卡的拓扑关系,NV 开头的链路才是高速互联 nvidia-smi topo -m

5.5 延迟类:平均延迟正常,P99 抖得离谱

现象:压测报告里平均延迟很漂亮,长尾延迟却是平均值的五到十倍,偶发几秒的尖刺,监控上看不出明显异常。

原因:几类问题混在一起。一是进程内存被换到磁盘,前向传播访问到缺页,触发秒级回读;二是请求长度分布有长尾,短请求和超长请求混在一个批次里,长的那条把整批拖住,KV Cache 还要反复申请释放;三是显卡降频,功耗墙或温度墙触发后频率掉档。

解决:关掉交换分区或把交换倾向调到很低,关键进程用锁页内存;按长度分桶调度,把超长请求单独走一条通道;固定 CPU 调频策略,给显卡留足散热余量;服务启动后先跑几轮预热请求,把缓存和编译产物都走一遍,避免第一个真实请求吃冷启动。

6. 渐进式验证:把部署风险拆成四级可回滚的台阶

一套部署方案最容易出问题的地方,是一步到位直接上生产集群,然后在一个巨大的黑匣子里找故障。更稳的做法是拆成四级台阶,每一级都有明确的通过判据,失败就退回上一级,把问题锁在一个小范围里。

| 台阶 | 验证目标 | 通过判据 | 失败时先看哪里 | | 一级:CPU 冒烟 | 权重完整、分词器正确 | 贪心解码结果可复现 | 配置文件、分词器版本 | | 二级:单卡短上下文 | 数值正确性 | 输出与参考实现对齐 | 量化配置、计算精度 | | 三级:单卡长上下文 + 并发 | 显存曲线与延迟拐点 | 目标并发下无溢出、长尾可控 | KV 增长、碎片、批调度 | | 四级:多卡 / 集群 | 通信效率与调度 | 扩展比在 0.7 以上 | 拓扑、通信库、请求路由 |

这四级的意义在于把变量隔离开。一级不碰显卡,出问题一定是模型文件或配置;二级只碰单卡,出问题在数值精度;三级引入并发和长度,出问题在显存管理;四级引入通信,出问题在网络和调度。跳过任何一级,故障域就会成倍放大。

至于设计取舍,我自己的判断标准是三条。第一,容量和延迟不可兼得时优先保延迟——量化、卸载、超长上下文这三件事都在拿延迟换容量,而线上服务对长尾的容忍度远低于对吞吐的容忍度。第二,并行度不超过单机高速互联域——跨机做张量并行省下的显存,会被通信开销成倍收回去。第三,任何优化都要有可回滚的开关——量化的位数、卸载的层级、批次的上下限,全部做成配置项,出问题能在不改代码的前提下切回去。

踩过的坑是:曾经为了把 32B 塞进两张卡,把张量并行和参数卸载叠在一起用,本地小流量测下来延迟还能接受,一上量长尾直接失控,回头查了半天才定位到是卸载层在并发下反复搬运。从那以后我每次做容量规划,都强制把峰值并发下的三笔账重算一遍,并且在压测里单独看 P99 而不是平均值。希望帮到你。

本文还有配套的精品资源,点击获取