灵魂拷问:大模型推理为什么要PD分离?看完这篇你就知道了!!

📅 2026/8/27 14:46:40
灵魂拷问:大模型推理为什么要PD分离?看完这篇你就知道了!!
前言随着DeepSeek爆火面试中也越来越高频出现因此训练营也更新了DeepSeek系列技术的深入拆解。包括MLA、MTP、专家负载均衡、FP8混合精度训练Dual-Pipe等关键技术力求做到全网最硬核的解析~在 LLM 推理计算中 Prefill 和 Decode 两个阶段的计算/显存/带宽需求不一样通常 Prefill 是算力密集Decode 是访存密集。一些场景中 P 和 D 两者分开计算可提升性能。vLLM 是一种主流的推理框架本文主要围绕其 PD 分离场景做讨论。PD 分离方法出现的简单推演先是有整体部署的方案一个推理服务实例里面起一个模型适合数据长度固定场景推理服务可通过增加 batch size 提升吞吐/性能。但这种方式对于 Transformer 架构里面包含自回归计算过程效率却并不高。为了提升资源效率在大语言模型中设计出了 KV cache 减少重复计算模型计算过程能拆成两步prefill一次运算算力消耗大decode自回归多次迭代存储密集这两个过程如果放一个实例硬件资源固定中会出现 P 阶段显存利用率低、D 阶段算力使用不高的问题为了进一步提升系统效率又衍生出了 P 和 D 分开计算的解决方法。01、vLLM PD分离方案现状开源的 vLLM0.8.x 版本的 PD 分离功能依靠 KV transfer 来完成代码 PR能够支持 1P1D 场景的运行。https://github.com/vllm-project/vllm/pull/10502其工作关键顺序执行 P 和 D 计算用一个 kv transfer 线程交换 kv cache 信息通过 proxy 控制交互过程。软件流程图如下所示这种方式是一种生产者producer和消费者consumer模式P 将运算完成的 kv 值以非阻塞的方式插入 buffer 中D 通过阻塞模式去获取 buffer 中的 kv 值。buffer 是双端队列LookupBufferbuffer 之间的数据传递需要通过 pipe 完成主要是解决远端数据传递Remote transfer pipe 可选择 pynccl 或者 mooncacke store。方案当前比较简洁所以操作部署比较简单。代码实现上只需要把 transfer 配置传递给 LLMP 与 D 基本相同区别在于 rank 信息不一样。参考vllm/examples/offline_inference/disaggregated_prefill.py# 相同的输入 prompts [ Hello, my name is, Hi, your name is, Tell me a very long story, ] sampling_params SamplingParams(temperature0, top_p0.95)# prefill 运算 ktc KVTransferConfig.from_cli( {kv_connector:PyNcclConnector,kv_role:kv_producer,kv_rank:0,kv_parallel_size:2} ) llm LLM(modelmeta-llama/Meta-Llama-3.1-8B-Instruct, kv_transfer_configktc, max_model_len2000, gpu_memory_utilization0.8) llm.generate(prompts, sampling_params) print(Prefill node is finished.) prefill_done.set()# decoder 运算 ktc KVTransferConfig.from_cli( {kv_connector:PyNcclConnector,kv_role:kv_consumer,kv_rank:1,kv_parallel_size:2} ) llm LLM(modelmeta-llama/Meta-Llama-3.1-8B-Instruct, kv_transfer_configktc, max_model_len2000, gpu_memory_utilization0.8) prefill_done.wait() outputs llm.generate(prompts, sampling_params)总体来看功能处于探索阶段所以支持功能比较少比如 xPyD、TP/PP、Chunk Prefill 等其调度未考虑负载均衡、集群性能等问题V1 版本上适配讨论中。02、设计需要考虑的问题PD 分离设计时需要考虑一些问题03、现有/可行方案的分析讨论1Connector-Base 方案该方案是社区讨论方案更新时间2025/3/25 在线文档https://docs.google.com/document/d/1uPGdbEXksKXeN4Q9nUm9hzotqEjQhYmnpAhidLuAsjk/edit?tabt.0#headingh.z11h6wpmv17d每个 vLLM 进程都创建一个 connector 链接器然后创建两类 connector 分别配置到 scheduler、worker 上面。Scheduler connector它负责调度 KV 缓存传输操作决定哪些标记tokens需要从连接器加载 KV 缓存或者将 KV 缓存保存到连接器。与调度器同进程Worker connector负责 kv cache 传输操作与 worker 同进程。异步传输执行步骤Step 1router 发送 prefill 请求到 prefiller 实例Step 2router 通知 prefiller_connector 给哪个 decoder 发送数据Step 3Prefiller 运行 P 运算并且把结果存到 P connector 的 buffer 里面Step 4Prefiller 发送pushesKV cache 到 D_connectorstep3 和 step4 并行执行Step 5D connector 通知 P connector 已获得 KV cache 值Step 6P connector 通知 routerKV 完成了 push 操作Step 7router 发送 decode 请求到 decoder 实例Step 8decoder 实例从 D connector 的 buffer 加载 KV cacheStep 9运行 decode 计算Scheduler 相关侧的代码修改connector 携带状态statefulconnector 可以调用 allocate_slots 和 freee将allocate_slots和free函数传递给连接器以便其能够为键值对KV缓存传输预留 GPU 缓冲区。这使得连接器能够透明地将外部键值对缓存注入到 GPU 中并将其作为前缀缓存块从而使调度器可以将它们视为普通的前缀缓存块进行处理。改动点get_computed_blocks# In get_computed_blocks, we call the connector at the scheduler side (we call it scheduler # connector) to determine the KV cache of what tokens need to be loaded from the connector: def get_computed_blocks( self, request: Request) - tuple[list[KVCacheBlock], int] # After querying the GPU prefix cache computed_blocks, num_computed_tokens self.connector.get_external_prefix_cache_blocks( request, computed_blocks, num_computed_tokens, ) return computed_blocks, num_computed_tokens改动点scheduler_output Before returning the scheduler output, we call the scheduler connector to: calculate the KV cache of which tokens need to be saved to the connector and prepare the metadata to tell the worker connector the KV cache of what tokens need to be saved / loaded.scheduler_outputSchedulerOutput(scheduled_new_reqsnew_reqs_data,scheduled_cached_reqsresumed_reqs_datarunning_reqs_data,num_scheduled_tokensnum_scheduled_tokens,total_num_scheduled_tokenstotal_num_scheduled_tokens,scheduled_spec_decode_tokensscheduled_spec_decode_tokens,scheduled_encoder_inputsscheduled_encoder_inputs,Worker 侧相关侧的代码修改在模型运行之前异步加载所有层的键值对KV在模型运行之后等待所有层的键值对保存完成。在 attention 操作中在执行 attention 之前检查该层的键值对是否加载完成并在注意力操作之后发起键值对缓存的保存改动点gpu_model_runner.py# In gpu_model_runner.py, prepare the worker connector’s input in _prepare_inputs() def _prepare_inputs( self, scheduler_output: SchedulerOutput, ) - tuple[FlashAttentionMetadata, torch.Tensor, Optional[SpecDecodeMetadata]]: # This will reset the state of connector self.connector.parse_connector_meta(scheduler_output.connector_meta) ...... # Run the decoder. # Use persistent buffers for CUDA graphs. with set_forward_context(attn_metadata, self.vllm_config, self.connector): hidden_states self.model( input_idsinput_ids, positionspositions, intermediate_tensorsintermediate_tensors, inputs_embedsinputs_embeds,)改动点forward_context.py/set_forward_context()In forward_context.py/set_forward_context(), asynchronously fire KV cache load operation layer-by-layer before model execution, and blocking wait for the KV cache save operation after model execution:connector.start_load_kv_async(static_forward_context)try:yieldfinally:connector.wait_for_save_kv()改动点vllm/attention/layer.py# In vllm/attention/layer.py: check for load before attn and async save after attn: forward_context: ForwardContext get_forward_context() attn_metadata forward_context.attn_metadata self_kv_cache self.kv_cache[forward_context.virtual_engine] forward_context.connector.wait_for_load_kv(self) self.impl.forward(self, query, key, value, self_kv_cache, attn_metadata, outputoutput) forward_context.connector.start_save_kv_async(self)V1 版本的适配考虑解决方案Woosuk这个方案是在 P 和 D 上各创建一个 scheduler。优势更好适配 Chunked prefill disaggregated prefill。不足需要维护两类 scheduler。2英伟达 Dynamo 方案Dynamo 架构分为内外两层外层根据全局资源的情况完成请求的分发内层以 PD 分离为基础构造实例。Dynamo 通过 KV Cache 为核心链接内、外层系统架构图如下运行逻辑外层运行逻辑http 请求接受、分发请求、P/D 计算、请求返回外层需要处理全局计算资源。关键要素前端Frontend采用 OpenAI 类似 http 请求服务响应用户请求路由Router根据一定的策略把前端请求分配到不同 worker处理单元Workers 有 prefill/decode 两种 worker 处理 LLM 计算逻辑部署示意图请求分配到 woker id 后到进入分离式服务disaggregation serverdecode 和 prefill 在不同 worker 中他们之间通过 KV block 完成信息交互两种 work 之间交互处理流程如下图所示。Dynamo 运行高效的一个关键是借助了 NIXL 来提速。The key to high-performance disaggregation is efficient KV transfer. Dynamo leverage NIXL to transfer KV cache directly from the VRAM of prefill engine to the VRAM of decode engine. In addition, the KV transfer is non-blocking, allowing GPU forward pass to serve other requests in addition to the KV transfer.在 P 与 D 之间有个队列Queue主要用于负载均衡当存在远端交互时流程图如下所示步骤 1当 worker 收到一个请求时它首先决定是否在本地还是远程进行 prefill 处理并分配 KV block。如果是在远程进行计算它会将一个远程 prefill 请求推送到预填充队列prefillQueue中。步骤 2P worker 然后从 prefillQueue 拉取请求读取 KV block并计算 prefill 内容并将计算后的block值写回。步骤 3Decode worker 完成剩余的解码工作。3Mooncake 集成方案mooncake 是 PD 分离应用比较早也是规模比较大的成功例子关键核心是构建了一个 KV Cache 为中心的解决方案。Transfer Engine支持 TCP、RDMA InfiniBand/RoCEv2/eRDMA/NVIDIA GPUDirect和 NVMe - over - FabricNVMe - of协议提供从 DRAM、VRAM 或 NVMe 转移数据的统一接口隐藏与硬件相关的技术细节。Mooncake StoreMooncake 存储基于 Transfer Engine为 LLM 推理提供分布式 KV Cache 存储引擎提供对象级 APIPut、Get和Remove。方案中的 put() 和 get() 操作具体如下put() 操作涉及将 KV 缓存从 GPU 分页缓存传输到推理机的本地 DRAM 中。get() 操作包括两个步骤。在第一步中当 vLLM 前端接收到请求时它会向 Mooncake Store 发送一个拉取请求以异步方式将KV缓存拉取到其本地 DRAM 中。在第二步中在将请求添加到运行队列之前KV 缓存会从本地 DRAM 传输到 GPU 分页缓存。整体工作流程如下vLLM 与 Mooncake 的整合方案设计文档https://docs.google.com/document/d/1Ab6TMW1E2CdHJJyCrpJnLhgmE2b_6leH5MVP9k72sjw/edit?tabt.0调度层设计proxy 系统结构可以采用如下形式控制流与数据流分离。通过独立的 KV Cache 存储来分离预填充prefill节点和解码decode节点。这种解耦确保了预填充节点无需等待解码节点来获取 KV Cache。控制路径可以通过直接的 HTTP 调用或消息队列来实现。设置完成后解码实例Decode instances会从消息队列或直接从 API 调用中接收请求然后从 KV Cache 存储中获取 KV Cache跳过预填充阶段直接进行解码阶段。考虑实现功能1.零拷贝的方式直接读取并传输 KV 缓存到其目标位置目前 KV 缓存是从 vLLM 的内部页面缓存中复制到一个临时缓冲区然后再发送到 Decode node 或 KV 缓存存储中。这一过程涉及多轮数据复制在高性能场景下可能会出现问题。2.全局 KVCache 重复使用在 DisaggLLMEngine 里面维护一个全局 prefix提升系统效率。未完待续4SGLang 方案SGLang 的 PD 分离方案社区版是在原有的调度事件循环**scheduling event loop**的基础上增加一个非阻塞式的发送者/接受者模式完成 PD 分离的实现。当前合入的 PR 来看已完成相应接口设计。虽然内容还比较简单但可以参考其框架实现的思路。具体的实施在原有的 P/D 实例中增加相应的服务Server来处理请求同一个请求创建两个信息跟踪角色在 Prefill 里面创建**“sender”、在 Decode 创建“reveiver”。**根据 Prefill 和 Decode 实例不同创建不同处理队列并把处理流程分为了多个阶段每个阶段有一个队列负责。这两个角色在各自的队列里面流转。Prefill创建三个队列引导队列BootstrapQueue为请求创建 sender 实例并与 decode 进行握手等待 decode 的资源准备kv等待队列Waiting Queue等待资源进行 P 前向运算完成 P 的前向运算后事件进入下一个队列非阻塞查询队列Infight Queue查询 KV 是否传输完成完成后返回请求Decode 创建三个队列资源分配队列PreallocQueue为请求创建 reveiver 实例与 Prefill 握手并创建 KV 存储KV 传输队列TransferQueue队列中请求需要获取 Prefill 计算完成后的 KV 值单个请求实例获得 KV 值后传递到等待队列等待队列WaitingQueue等待凑批后进行 D 运算。把队列中的请求获得了 KV 值构建一个批次实例PrebuiltExtendBatch对批次进行融合后在进行 D 的前向计算。P 与 D 的互动机制创建 request 时P 与 D 会进行一次握手确认D 创建完成 KV 好后会通知 P让其 sender requset 进入等待队列当 P 计算完前向触发 KV 传输P 非阻塞的轮巡检查 KV 是否传输完成不直接与 D 互动目前 KV 值的传递采用非阻塞式的后端进程方式进行处理P 和 D 中分别用 KVSender、KVReceiver 进行数据的收发当前列出的传递方式逐层传递、按照 chunk 为单位传递。用非阻塞的传递不会影响主进程继续处理新的请求但可能存在网络流量冲突的问题目前未考虑/解决。整个 P/D 分离信息的处理流程是在 scheduler 里面所以需要增加 event loop 处理逻辑。python/sglang/srt/managers/scheduler.py参考https://github.com/sgl-project/sglang/pull/4654/files#diff-c3b8cc39d10c245933a25aa9c2fd6397f6b31ed8d85c0ecbb926c1f42afdd178defevent_loop_normal_disagg_prefill(self):passdef event_loop_normal_disagg_decode(self):pass**使用方式**server 启动新增了两个入参通过入参来选择 P 和 D 实例调度总入口应该是会通过 Bootstrap server port 来构建。# Disaggregation parser.add_argument( --disaggregation-mode, typestr, defaultnull, choices[null, prefill, decode], helpOnly used for PD disaggregation. prefill for prefill-only server, and decode for decode-only server. If not specified, it is not PD disaggregated, ) parser.add_argument( --disaggregation-bootstrap-port, typeint, defaultServerArgs.disaggregation_bootstrap_port, helpBootstrap server port on the prefill server. Default is 8998., )目前 SGLang 的 PD 官方版本还有许多工作带展开参考其 PD 分离的路标计划2025/3/31方案参考SGLang社区讨论方案在线文档更新时间2025/4/9https://docs.google.com/document/d/1rQXJwKd5b9b1aOzLh98mnyMhBMhlxXA5ATZTHoQrwvc/edit?tabt.0#headingh.i3s2t1j0e1ik最后为什么要学AI大模型当下⼈⼯智能市场迎来了爆发期并逐渐进⼊以⼈⼯通⽤智能AGI为主导的新时代。企业纷纷官宣“ AI ”战略为新兴技术⼈才创造丰富的就业机会⼈才缺⼝将达 400 万DeepSeek问世以来生成式AI和大模型技术爆发式增长让很多岗位重新成了炙手可热的新星岗位薪资远超很多后端岗位在程序员中稳居前列。与此同时AI与各行各业深度融合飞速发展成为炙手可热的新风口企业非常需要了解AI、懂AI、会用AI的员工纷纷开出高薪招聘AI大模型相关岗位。最近很多程序员朋友都已经学习或者准备学习 AI 大模型后台也经常会有小伙伴咨询学习路线和学习资料我特别拜托北京清华大学学士和美国加州理工学院博士学位的鲁为民老师给大家这里给大家准备了一份涵盖了AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频全系列的学习资料这些学习资料不仅深入浅出而且非常实用让大家系统而高效地掌握AI大模型的各个知识点。这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】AI大模型系统学习路线在面对AI大模型开发领域的复杂与深入精准学习显得尤为重要。一份系统的技术路线图不仅能够帮助开发者清晰地了解从入门到精通所需掌握的知识点还能提供一条高效、有序的学习路径。但知道是一回事做又是另一回事初学者最常遇到的问题主要是理论知识缺乏、资源和工具的限制、模型理解和调试的复杂性在这基础上找到高质量的学习资源不浪费时间、不走弯路又是重中之重。AI大模型入门到实战的视频教程项目包看视频学习是一种高效、直观、灵活且富有吸引力的学习方式可以更直观地展示过程能有效提升学习兴趣和理解力是现在获取知识的重要途径光学理论是没用的要学会跟着一起敲要动手实操才能将自己的所学运用到实际当中去这时候可以搞点实战案例来学习。海量AI大模型必读的经典书籍PDF阅读AI大模型经典书籍可以帮助读者提高技术水平开拓视野掌握核心技术提高解决问题的能力同时也可以借鉴他人的经验。对于想要深入学习AI大模型开发的读者来说阅读经典书籍是非常有必要的。600AI大模型报告实时更新这套包含640份报告的合集涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师还是对AI大模型感兴趣的爱好者这套报告合集都将为您提供宝贵的信息和启示。AI大模型面试真题答案解析我们学习AI大模型必然是想找到高薪的工作下面这些面试题都是总结当前最新、最热、最高频的面试题并且每道题都有详细的答案面试前刷完这套面试题资料小小offer不在话下这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】