大模型推理优化:Prefill 与 Decode 两阶段瓶颈及调优实践

发布时间:2026/10/28 14:15:54
大模型推理优化:Prefill 与 Decode 两阶段瓶颈及调优实践 你大概见过这种曲线服务刚上线时首 token 延迟两百毫秒上下看着挺体面并发一上来这个数字开始抖抖到两秒、五秒可每 token 的输出延迟却稳得很四五十毫秒没怎么变。运维查了网卡、查了显存占用、查了卡上的利用率指标都不难看但用户就是在反馈卡。遇到这种情况我一般先不碰硬件也不急着换推理框架而是把一次请求拆成两个性质完全不同的阶段来看——**Prefill预填充**和Decode解码。这两个词在大模型推理里出现的频率极高但真正搞清楚它们在计算上差在哪、为什么要用两套策略去伺候的人并不多。Prefill 是一次性把整段 prompt 塞进网络一次前向算完所有 token 并行过模型Decode 是每步只吐一个 token一步一循环直到遇到结束符。前者是典型的算力密集型后者是典型的访存密集型两者的瓶颈、优化手段、调参方向几乎是相反的。你把它们当成一件事来调用同一套批处理策略、同一个显存预算、同一种量化方案结果必然是两头不讨好。这篇内容就是把这层窗户纸捅破从矩阵乘的形态讲到显存带宽的账从 KV Cache 怎么长出来讲到分块预填充和 PD 分离怎么落地中间插一些我自己踩过的坑和可以直接抄的压测脚本。不需要你先懂 CUDA 算子只要你会写几行 Python、跑过模型服务就能顺着往下读如果你已经在做推理部署第 4、5 部分应该能直接对上你监控里的那些异常曲线。1. 把两个阶段掰开看一次请求到底发生了什么1.1 从输入到第一个 tokenPrefill 到底干了什么用户发来一句话比如三百个 token推理引擎收到的是一整个 token 序列而不是一个一个喂进去。Prefill 做的事情就是把这个长度 300 的序列当成一个整体走一遍完整的前向embedding 查表得到 300 个向量每一层做自注意力、做前馈网络一路往上最后只取最后一个位置的输出去算下一个 token 的概率分布。注意这里的关键——300 个位置全部参与了计算但它们之间不是独立的自注意力里的每个位置都要看到它前面所有位置的信息所以这一层的计算量随序列长度是平方级增长的。用生活化的说法Prefill 像是一次性把一整篇文章读进去理解读的时候前后文互相对照读完整篇才写出第一个字。这个过程里模型权重只需要被读取一遍但是要参与大量的乘加运算所以它对算力的胃口极大对显存带宽反而不那么敏感。这也是为什么在 Prefill 阶段你会看到加速卡的算力利用率SM 占用、Tensor Core 活跃度能跑到比较高的水位而显存控制器没那么忙。还有一点容易被忽略Prefill 阶段要一次性写入全量 KV Cache。300 个 token每一层都要存下它们的 Key 和 Value这些数据后面 Decode 阶段要反复读取所以 Prefill 不只是算还是一次大规模的显存写操作。这个写的量级我在 2.4 节会算给你看先记住这个动作的存在。1.2 从第二个 token 开始Decode 为什么是一个绕不开的循环第一个 token 吐出来之后流程就变了。第二步的输入只有一个 token上一步生成的那个但它要跟前面 300 个 token 的 KV 拼在一起做注意力。这里就体现出 KV Cache 的价值了前面 300 个 token 的 K 和 V 在 Prefill 阶段已经算好存下来了Decode 每一步只需要算当前这一个 token 的 Q、K、V然后把新 K、V 追加到缓存尾部再用当前 Q 去跟全部 301 个位置做注意力。如果没有 KV Cache每一步都要把全部历史重新算一遍代价会大到没法用。关键是这个动作要重复几百上千次。每生成一个 token你就要把整个模型的权重从头到尾读一遍但在当前这一步里真正参与计算的新 token 只有一个。权重读了几十 GB算的却只是一次矩阵乘向量这就是 Decode 阶段的根本矛盾——算力大量闲置带宽被榨干。你可以把它想象成每次只为了查一个字就把整本字典从头翻一遍。字典的内容权重没有变翻的动作显存读取却是实打实的成本。1.3 两个阶段的本质差异用一张表说清楚把上面的描述收敛成对照表后面所有的优化手段几乎都能从这张表里推出来维度Prefill预填充Decode解码输入形态长度 T 的完整序列单 token每步计算量与 T 成正比线性层 与 T² 成正比注意力每步固定与历史长度成正比注意力部分权重读取一次前向读一遍每生成一个 token 读一遍核心瓶颈算力compute-bound显存带宽memory-bound矩阵形态大 GEMM瘦高矩阵乘胖矩阵GEMV 退化矩阵乘向量KV Cache批量写入逐步追加、全量读取批处理收益中等主要摊薄权重读取极高权重读取被 batch 摊薄优化重点提高算力利用率、减少无效计算提高带宽利用率、增大有效 batch量化敏感度中注意激活值精度高权重位宽直接决定速度这张表建议你贴在工位上。后面遇到任何优化方案先问一句它作用在哪一列答案清楚了方案值不值得试也就清楚了。比如有人跟你说把权重量化到 4bit 能提速一倍你心里应该有数这是冲着右列去的对 Prefill 的收益非常有限甚至可能因为反量化开销反而变慢。2. 算力账本为什么 Prefill 吃算力、Decode 吃带宽2.1 算术强度一个能解释大部分现象的比值要判断一段计算是吃算力还是吃带宽标准工具是算术强度Arithmetic Intensity定义很简单每从显存里搬一个字节的数据能顺带做多少次浮点运算。单位是 FLOP/Byte。拿它去和硬件的脊点Ridge Point比脊点等于硬件峰值算力除以峰值带宽超过脊点的计算就是算力受限低于脊点的就是带宽受限。拿一块常见的数据中心加速卡举例FP16 稠密算力按 300 TFLOPS 估显存带宽按 2000 GB/s 估脊点大约是 150 FLOP/Byte。意思是每搬 1 个字节的数据你得做够 150 次浮点运算才算没浪费这块卡的算力。现在看 Prefill。假设 7B 参数模型输入 2048 个 tokenFP16。权重总量约 13.4 GB只读一遍计算量按经验公式 2 × 参数量 × token 数估2 × 6.7e9 × 2048 ≈ 2.74e13 FLOPs也就是 27.4 TFLOP。算术强度 2.74e13 / 1.34e10 ≈ 2045 FLOP/Byte。远远高于 150妥妥的算力受限。所以 Prefill 该做的事是把算力榨干而不是省显存读取。再看 Decodebatch 1。每生成一个 token权重还是读 13.4 GB计算量只有 2 × 6.7e9 ≈ 1.34e10 FLOPs。算术强度 1.34e10 / 1.34e10 1 FLOP/Byte。连脊点的百分之一都不到。这就是为什么单请求跑 Decode 的时候你看显卡的算力利用率会低得离谱而显存带宽可能已经跑满。这组数字带来的直觉是Decode 阶段要提升吞吐唯一的出路是把 batch 做大让同一份权重读取服务多个请求把算术强度拉回来。batch 16 时算术强度能到 16虽然离脊点还远但吞吐已经是 batch 1 的十几倍了。2.2 Prefill 的 FLOPs 拆解线性层和注意力各占多少很多人以为 Prefill 的耗时大头永远是注意力这个认知在短上下文下是错的在长上下文下才对。我们把 2.74 TFLOP 拆开看。线性层部分QKV 投影、输出投影、FFN 的两层总共接近 2 × 参数量 × T就是上面算的 27.4 TFLOP 里的大头。注意力部分单独算QK^T 的乘加是 2 × T² × d加权求和 AV 也是 2 × T² × d两层注意力矩阵乘加起来 4 × T² × d再乘层数 L。代入 T 2048、d 4096、L 324 × 2048² × 4096 × 32 ≈ 2.2e12也就是 2.2 TFLOP。占比约 8%。但注意力是平方增长的线性层是线性增长的。把 T 拉到 8192注意力部分乘 16 变成 35 TFLOP线性层只乘 4 变成 110 TFLOP注意力占比升到 24%。再拉到 32768注意力变成 560 TFLOP线性层 440 TFLOP注意力占比过半。这份账解释了为什么同一个服务短 prompt 场景跑得好好的一接长文档场景就崩。不是你的框架不行是计算结构变了原来可以忽略的那一项变成主项了。对应的优化手段也就不一样短上下文该优化 GEMM 的分块和融合长上下文得先想办法把注意力的平方项压下去。2.3 Decode 的访存账本单卡吞吐的理论上限Decode 每一步要读什么权重 13.4 GB加上当前上下文的 KV Cache。上下文 2048 时KV Cache 大小在 1 GB 量级算法见下节总计约 14.4 GB。显存带宽 2000 GB/s理论耗时 14.4 / 2000 ≈ 7.2 毫秒换算成单请求速度约 139 token/s。这是理论天花板实际因为流水线气泡、kernel 启动开销、带宽利用率达不到 100%通常是这个数字的三分之一到二分之一也就是 40 到 70 token/s。你线上看到的单请求解码速度基本就在这个区间。再看 batch 16。权重还是读 13.4 GB但 KV 变成 16 × 1 GB 16 GB总计 29.4 GB耗时 14.7 毫秒。注意这 14.7 毫秒里16 个请求各自都推进了一个 token所以吞吐是 16 / 0.0147 ≈ 1088 token/s是单请求的 7.8 倍。这就是连续批处理的价值所在。顺便说个反直觉的现象batch 增大时单个请求的 TPOT每 token 延迟实际上是变差的。batch 1 时 7.2 msbatch 16 时每个请求还是要等 14.7 ms 才拿到自己的那个 token。吞吐和延迟在 Decode 阶段是明确的 trade-off不是双赢。这一点在 4.1 节还要展开。2.4 KV Cache 的显存账被大多数人低估的那部分KV Cache 到底多大公式是每 token 每层存一对 K 和 V每个张量的元素数是 n_kv_heads × head_dim数据类型 FP16 占 2 字节。所以每 token 大小 2K 和 V× n_kv_heads × head_dim × 2 字节 × 层数以 32 层、32 个 KV 头、head_dim 128 的模型为例2 × 32 × 128 × 2 × 32 524288 字节正好 512 KB/token。2048 个 token 就是 1 GB8192 个 token 就是 4 GB32768 token 就是 16 GB已经超过模型权重本身了。如果模型用了分组查询注意力GQA把 KV 头从 32 组减到 8 组每 token 直接降到 128 KB同样的显存能撑四倍上下文。这是现在主流模型几乎都上 GQA 的核心原因省的不是算力是 KV Cache。做容量规划时可以这样估一块 80 GB 显存的卡模型权重 13.4 GB留给 KV 的按 60 GB 算还要留激活值和临时缓冲区如果每 token 128 KB能装下约 46 万个 token 的总量。这个总量是所有并发请求共享的8 个并发各 8192 上下文就用掉 65 万个 token直接爆。这就是为什么长上下文并发一高就 OOM而单纯把 batch 调小只是把问题往后推。3. 算子视角从 GEMM 到 GEMV 的性能落差3.1 Prefill 阶段是大矩阵乘的堆叠Prefill 里最主要的计算形态是 GEMM通用矩阵乘。以 FFN 层为例输入是一个 [T, d] 的矩阵权重是 [d, 4d] 的矩阵输出 [T, 4d]。T 动辄几百上千两个维度都足够大GPU 的 Tensor Core 可以做很好的分块数据复用率高算力能跑到峰值的相当比例。QKV 投影同理输入 [T, d] 乘权重 [d, 3d]也是大 GEMM。这类计算的优化方向很成熟算子融合把 QKV 三个投影合成一个、分块大小调优、用更宽的累加精度。你去看推理框架的日志Prefill 阶段耗时基本都花在少数几个大 kernel 上每个 kernel 的耗时是可以预测的优化空间主要在于减少 kernel 数量和减少中间结果的显存往返。3.2 Decode 阶段退化成矩阵乘向量Decode 的输入只有一个 token也就是 [1, d] 的向量去乘 [d, 4d] 的权重矩阵。这在形态上叫 GEMV矩阵乘向量。问题是 GEMV 的访存模式极不友好权重的每一行只用一次没有任何复用相当于纯粹把权重从显存搬到计算单元搬完就扔。这在 GPU 上是最浪费的场景之一。这也是为什么很多框架在 Decode 阶段会做批量化 GEMV把 batch 个请求的向量拼成 [B, d]退化成一个小 GEMM。B 越大越接近 GEMM 的效率。但 B 受限于显存和延迟要求实际很难做大通常在 8 到 64 之间徘徊。还有一个经常被忽视的成本kernel 启动开销。Decode 每生成一个 token都要走完全部层几十个 kernel 挨个启动。如果每个 kernel 只有几十微秒启动开销和调度开销的占比就很可观了。所以 Decode 阶段特别吃算子融合和CUDA Graph 捕获这类手段把整段前向固化成一张图一次性提交能省掉大量 CPU 侧的调度开销。这一点在 batch 小的时候尤其明显我自己实测过只加图捕获这一项小 batch 场景的 TPOT 能降一到两成。3.3 注意力部分两种截然不同的数据流动Prefill 的注意力是一整块下三角的因果掩码矩阵。T 个 query 对 T 个 key 做注意力掩码保证每个位置只能看到自己和前面。这个形态适合做分块计算把一个 T×T 的大矩阵切成小块只算下三角部分跳过被掩码的上三角。分块注意力的核心思路就是避免把整个 T×T 的注意力分数矩阵物化到显存里——那个矩阵在 T 8192 时是 8192² × 2 字节 ≈ 128 MB 每头每层物化出来直接爆。Decode 的注意力则简单得多当前这一个 query对长度为 L 的全部 key 做注意力。分数向量长度是 L不涉及平方项。但它是纯读取操作——要把 KV Cache 里 L 个位置的数据全读一遍。上下文 2048 时读 1 GB耗时 0.5 ms看着不多上下文 32768 时读 16 GB耗时 8 ms已经超过读权重的 6.7 ms 了。这时候 Decode 的瓶颈从权重带宽变成了KV 带宽。这就是长上下文解码慢的底层原因不是模型变复杂了是每步要搬的数据变多了。对应的手段有 GQA直接减少 KV 体积、KV 量化FP16 降到 INT8体积减半、稀疏注意力只读部分 KV、以及滑动窗口限制窗口大小老 KV 直接丢弃。每一样都是在拿精度或能力换速度没有免费的午餐。3.4 分页式 KV 管理与前缀复用传统做法是给每个请求预分配一块连续的显存放 KV长度按最大可能值留实际用不完也不给别人。这会造成严重的内部碎片——一个请求只要 200 token你按 8192 预留剩下 7992 的空位全浪费了。当并发上来显存被一堆半空的块占满明明还有余量却接不了新请求。分页式管理借鉴了操作系统的虚拟内存思路把 KV Cache 切成固定大小的块比如每块存 16 个 token 的 KV按需分配不要求物理连续用一张映射表记录逻辑位置到物理块的对应关系。好处有三个碎片几乎消失显存利用率能到 90% 以上请求长度可以动态增长不用预留最重要的是块可以共享。块共享这点衍生出一个非常实用的功能前缀缓存。很多线上场景的 prompt 有大量相同前缀比如固定的系统提示词、固定的知识库文档头。这些前缀算出来的 KV 完全一样没必要每个请求都重算一遍。分页管理下只要把相同前缀的块标成共享后续请求直接跳过这部分 Prefill只算新增的部分。我在一个客服类场景里试过系统提示词加固定说明有 1500 token命中前缀缓存后首 token 延迟从 600 ms 降到 180 ms效果立竿见影。注意前缀缓存的前提是前缀逐 token 完全一致差一个字符都会失效。实际落地时要把动态内容时间戳、用户名挪到 prompt 末尾否则命中率会低得让你怀疑人生。4. 工程实践把两阶段调顺手的几个关键动作4.1 首 token 延迟与单 token 延迟的取舍衡量推理服务质量两个核心指标必须分开看TTFTTime To First Token首 token 延迟主要由 Prefill 决定加上排队时间TPOTTime Per Output Token每 token 延迟主要由 Decode 决定加上调度间隔。用户体感上TTFT 决定点完之后多久有反应TPOT 决定吐字流不流畅。这两个指标是互相打架的。你真要压低 TTFT就得让 Prefill 优先跑把 Decode 请求挂起等着Preill 一跑就是几十上百毫秒这期间所有正在解码的请求全都停顿用户看到的就是打字机卡住。反过来你为了保证 TPOT 平稳就得限制 Prefill 的频率和批量TTFT 自然上去。我的经验是做分档交互式场景聊天、助手优先保 TPOT把 Prefill 切碎混进 Decode 的批里TTFT 允许 300 到 800 ms批处理场景文档摘要、批量翻译优先保吞吐Prefill 可以大批量跑满TTFT 到几秒也无所谓。这两类流量最好在入口就做好路由别混在一个池子里否则两边的指标都会难看。4.2 分块预填充让长 prompt 不再堵车分块预填充Chunked Prefill是我认为性价比最高的一个改动。做法很简单把一个长 prompt 的 Prefill 拆成若干块比如每块 512 个 token然后每一轮调度时把一块 Prefill 和当前所有 Decode 请求拼成一个混合批一起跑一次前向。这样做的效果是把原来一次独占式的大 Prefill变成多次小块 Prefill代价是 TTFT 稍微变长因为分了几轮收益是 Decode 请求几乎感受不到停顿TPOT 曲线会变得平滑。对于那些既要处理长文档又要同时服务多个短对话的服务这个改动能直接把尾延迟压下去一大截。参数选择上有讲究块太小调度次数变多每次的固定开销占比上升整体吞吐下降块太大Decode 被挤占的时间变长TPOT 抖动回来了。我的经验是从 512 开始试观察 TPOT 的 P99 和整体吞吐如果 P99 抖得厉害就往下调到 256如果吞吐掉得多就往上调到 1024。这个值和硬件、模型大小强相关没有一个通用最优解。4.3 连续批处理与显存水位控制连续批处理Continuous Batching解决的是一个批里最快的请求要等最慢的这个浪费问题。传统静态批处理一批里 8 个请求7 个已经生成完了在等第 8 个这 7 个占着显存不干活。连续批处理的做法是每生成一步就检查一次谁结束了立刻踢出去空出来的位置马上塞新请求进来。这个机制对 Decode 阶段意义巨大因为 Decode 的每一步是一个天然的调度点。但要注意一个细节新请求进来通常先要跑 Prefill如果它一进来就独占正在解码的请求又要停。所以连续批处理必须和分块预填充配合使用否则等于把堵车点从入口挪到了中途。显存水位控制是配套的另一个动作。引擎一般会设一个阈值比如显存占用的 90%超过就拒绝新请求或者排队。这个阈值不能设太满因为 Prefill 的激活值峰值比 Decode 高很多——处理一个 8192 token 的 Prefill中间激活值可能要吃几 GB如果你按 Decode 的用量把显存占到 95%一个长 prompt 进来直接 OOM。我一般留 10% 到 15% 的余量并且对超过某个长度的 prompt 单独做限流。4.4 量化与推测解码分别作用在哪一段量化怎么影响这两个阶段值得单独说因为很多人量化完发现没提速以为是量化没用其实是作用错了地方。权重量化比如 4bit、8bit 只量化权重激活值还是 FP16主要收益在 Decode。因为 Decode 是带宽受限权重从 16bit 降到 8bit读取量直接减半速度理论上接近翻倍实测常见 1.4 到 1.8 倍。而 Prefill 是算力受限权重量化省下的带宽它用不上反而要额外做反量化可能还慢一点。权重加激活全量化W8A8才有可能同时惠及 Prefill因为激活也变成低精度后矩阵乘可以用低精度的 Tensor Core算力翻倍。但这条路对精度的影响要仔细评估特别是激活值的离群点问题很多模型直接上 W8A8 会掉点得厉害需要配合平滑或裁剪手段。推测解码Speculative Decoding是另一个思路用一个小模型快速草拟几个 token再用大模型一次性并行验证。它本质上是把 Decode 的多个串行步骤转化成一次 Prefill 形态的并行验证——因为验证阶段可以把 k 个候选 token 当成一个长度为 k 的序列一次性过模型这正好是 Prefill 的形态。所以推测解码特别适合 Decode 带宽打满、算力闲置的场景能把闲置的算力利用起来。代价是要多一份小模型的权重显存并且在草稿命中率低的时候收益归零。4.5 一套可复现的压测方法调参不能靠感觉得有一套能量化 TTFT 和 TPOT 的压测。下面这段是我常用的骨架思路是流式接收、记录首块到达时间和总耗时然后取中位数和 P99 看稳定性import time import statistics def bench_one(client, prompt, max_new_tokens128): t0 time.perf_counter() ttft None n_tokens 0 for chunk in client.stream(prompt, max_tokensmax_new_tokens): now time.perf_counter() if ttft is None: ttft now - t0 # 首块到达 n_tokens 1 total time.perf_counter() - t0 # 减去 TTFT 得到纯解码耗时再除以生成的 token 数 tpot (total - ttft) / max(n_tokens - 1, 1) return ttft, tpot def bench(client, prompt, runs30): ttfts, tpots [], [] for _ in range(runs): a, b bench_one(client, prompt) ttfts.append(a) tpots.append(b) ttfts.sort(); tpots.sort() p99 lambda xs: xs[int(len(xs) * 0.99) - 1] return { ttft_median: statistics.median(ttfts), ttft_p99: p99(ttfts), tpot_median: statistics.median(tpots), tpot_p99: p99(tpots), }压测时有几个细节必须控制住否则数据没有可比性。第一固定输入长度和输出长度别让模型自由发挥不然每次生成的 token 数不同TPOT 没法比。第二并发梯度测试从 1 并发测到目标并发每一档都记录你会看到一条清晰的曲线并发上升吞吐先涨后平TTFT 和 TPOT 单调上升拐点就是这套配置的合理水位。第三区分冷启动和热态第一次请求要加载权重、要编译图数据不能用。还有一点压测客户端本身别成为瓶颈。Python 的同步 HTTP 客户端在高并发下会被 GIL 和连接管理拖垮测出来的延迟里有一大半是客户端自己的开销。并发超过几十就要用异步客户端或者多进程压我吃过这个亏一度以为服务端有问题查了两天才发现是压测脚本自己在排队。5. 问题排查实录那些监控看不出原因的卡顿5.1 症状速查表线上一旦出现延迟异常先对照这张表定位能省掉大量瞎猜的时间症状大概率原因优先检查项TTFT 高但 TPOT 正常Prefill 排队、批太大、前缀缓存没命中排队深度、Prefill 批大小、缓存命中率TTFT 正常但 TPOT 抖动Prefill 抢占、块大小设置不当分块预填充是否开启、块大小TTFT 和 TPOT 同时劣化显存接近上限、触发换页或拒绝显存水位、KV 块分配失败率长 prompt 直接超时激活值峰值 OOM、单请求限流最大输入长度限制、显存余量并发上不去但显存没满KV 碎片、块分配器配置过小块大小、可用块数量量化后吞吐没变化量化只作用在权重瓶颈在算力侧是 Prefill 还是 Decode 慢单请求快、多请求慢批处理未生效或 batch 上限太低连续批处理开关、最大批大小首字很快但中途卡住KV 缓存被回收、请求被抢占重算抢占策略、优先级配置这张表不是穷举但覆盖了我遇到过的大部分情况。用法是从左列找最像的然后按右列逐项排除通常两三轮就能定位。5.2 几个反直觉的坑第一个坑加大 batch 不一定提升吞吐。很多人看到吞吐不够就把最大批大小往大了调结果发现吞吐反而降了。原因是 batch 大到一定程度后KV Cache 的读取量超过了权重读取量边际收益递减更糟的是显存接近上限块分配失败率上升触发请求抢占和重算实际干了双份活。我一般的做法是找到那条吞吐曲线的平台段停在平台的 80% 位置留出余量应对突发。第二个坑Prefill 的耗时不是线性的。有人按每 token 均摊去估算觉得 4096 token 的 prompt 应该是 2048 的两倍耗时。实际不是因为注意力的平方项在长上下文下占比上升4096 的 Prefill 耗时可能是 2048 的两倍半甚至三倍。做容量规划时一定要按实际长度压测别用线性外推。第三个坑流式输出的第一个块不一定是真首 token。有些客户端或网关会先发一个空的心跳块或者角色声明块你在客户端计时会把这段时间算进 TTFT测出来的数字比服务端真实值高出一截。排查时要在服务端也埋一份计时两边对不上就说明中间有额外缓冲。这个问题很隐蔽因为曲线看起来只是TTFT 偏高你会一直往服务端查。第四个坑KV Cache 量化之后长上下文的精度下降比短上下文明显得多。因为 KV 量化误差会随着读取的位置数量累积上下文越长注意力加权时引入的偏差越大。短问答场景测不出问题一上长文档就发现模型开始记不住前面的内容。要做 KV 量化必须用长上下文任务做验证不能只跑短 prompt 的评测集。第五个坑多卡张量并行的通信开销在 Decode 阶段占比极高。张量并行把每一层的矩阵乘切到多卡上每层要做两次全归约。Prefill 阶段计算量大通信能被掩盖Decode 阶段每层的计算只有几十微秒通信延迟直接暴露出来。所以你会看到 4 卡部署的 Prefill 快了 3 倍多Decode 只快了 1.5 倍。小模型做 Decode 尤其不划算宁可单卡多实例。最后分享一个我觉得最实用的观察方式把 TTFT 和 TPOT 画成两条随时间变化的曲线叠上并发数。正常情况下三条线的形状应该是同步的——并发涨、两条延迟涨、并发落、延迟落。如果是 TTFT 跟着并发涨但 TPOT 是平的问题在入口调度如果 TPOT 出现周期性尖峰而并发没变多半是分块预填充的块太大或者在跑定期任务如果两条线一起抖且幅度很大去看显存水位十有八九是块分配器在挣扎。这套看图方法比翻日志快得多尤其是在你没时间深挖的时候能让你在几分钟内把范围缩到一两个模块。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询