Rust 异步推理管线的流水线设计:Prefill 与 Decode 阶段的并行化调度引擎
Rust 异步推理管线的流水线设计Prefill 与 Decode 阶段的并行化调度引擎一、推理流水线的利用率困局50% 时间在等 I/O大模型推理服务中Prefill 阶段首次处理输入 prompt和 Decode 阶段逐 token 生成输出的计算特征截然不同。Prefill 是计算密集型——需要处理所有输入 token 的 Attention、投影和 FFN。Decode 是访存密集型——每次只处理 1 个 token大部分时间等待 HBM 数据。在单请求串行执行模式下这两种阶段交替出现GPU 利用率呈锯齿状波动Prefill 阶段 GPU 满负荷 ~3ms然后 Decode 阶段 GPU 空转 ~2ms再下一个 Prefill...平均利用率约 50%~60%。如果能让不同请求的 Prefill 和 Decode 阶段交替执行——一个请求在执行 Decode 时另一个请求执行 Prefill——GPU 的 SM 和显存带宽可以更充分地利用。这就是异步推理管线的核心思想。二、Prefill-Decode 分离调度的数据流模型graph TB subgraph 请求队列 R1[Request-1: Prefill pending] R2[Request-2: Decoding] R3[Request-3: Prefill pending] end subgraph 调度引擎 S{Schduler} S --|选择 Prefill 请求| P[Prefill Slot] S --|选择 Decode 请求| D[Decode Slots] end subgraph GPU 执行 P -- G1[Prefill 批次br/(Batch sizeN)] D -- G2[Decode 批次br/(Batch sizeM)] end subgraph KV Cache G1 -- KC[写入 KV Cache] G2 -- KC[读取 KV Cache] end R1 -- S R2 -- S R3 -- S style S fill:#1a1a2e,stroke:#e94560,color:#fff style P fill:#16213e,stroke:#0f3460,color:#fff style D fill:#16213e,stroke:#0f3460,color:#fff调度引擎需要协同两个独立的调度维度时间维度的交错Prefill 和 Decode 交替提交 GPU kernel让两种计算模式互补资源需求批次维度的聚合多个请求的 Prefill 可以批量执行连续批处理多个请求的 Decode 也可以批量执行关键约束是 KV Cache 的一致性Prefill 写 KV CacheDecode 读 KV Cache。在 Prefill 和 Decode 交替执行时必须确保 Prefill 写入的 KV Cache 对下一个 Decode 可见。这需要通过 CUDA Stream 的同步原语保证。三、异步推理调度引擎的 Rust 实现use std::collections::{VecDeque, HashMap}; use std::sync::Arc; use tokio::sync::{mpsc, Semaphore, Notify, RwLock}; use std::time::Instant; /// 推理请求的状态机 #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RequestState { /// 等待 Prefill PendingPrefill, /// Prefill 执行中 Prefilling, /// 等待 DecodePrefill 完成KV Cache 已就绪 PendingDecode, /// Decode 执行中 Decoding, /// 已完成 Completed, } /// 推理请求 pub struct InferenceRequest { pub id: u64, pub tokens: Vecu32, pub state: RequestState, /// token 生成的最大数量 pub max_new_tokens: usize, /// 已生成的 token 数 pub generated_count: usize, /// 请求到达时间 pub arrived_at: Instant, /// 输入 token 数量 pub input_len: usize, } /// 调度配置 pub struct SchedulerConfig { /// Prefill 的最大批次大小 /// 为什么限制 max_prefill_batch /// Prefill 的计算量是 Decode 的 input_len 倍 /// 过大的 Prefill 批次会显著增加延迟 pub max_prefill_batch: usize, /// Decode 流的最大并发数 pub max_decode_streams: usize, /// 是否允许 Prefill 抢占 Decode pub prefill_preemption: bool, } /// 异步推理调度器 pub struct AsyncInferenceScheduler { /// Prefill 请求队列 prefill_queue: RwLockVecDequeInferenceRequest, /// Decode 请求队列 decode_queue: RwLockVecDequeInferenceRequest, /// 活跃的 Decode 请求 active_decodes: RwLockVecInferenceRequest, /// Decode 并发槽位 /// 为什么用 Semaphore 控制并发 /// 每个 Decode 流占用固定的显存KV Cache /// Semaphore 确保活跃 Decode 不超过显存容量 decode_slots: ArcSemaphore, /// Prefill 可用的通知 prefill_ready: Notify, config: SchedulerConfig, } impl AsyncInferenceScheduler { pub fn new(config: SchedulerConfig) - Self { Self { prefill_queue: RwLock::new(VecDeque::new()), decode_queue: RwLock::new(VecDeque::new()), active_decodes: RwLock::new(Vec::new()), decode_slots: Arc::new(Semaphore::new(config.max_decode_streams)), prefill_ready: Notify::new(), config, } } /// 接收新请求并路由 pub async fn submit_request(self, mut request: InferenceRequest) { request.state RequestState::PendingPrefill; request.arrived_at Instant::now(); self.prefill_queue.write().await.push_back(request); // 通知调度线程有新任务 self.prefill_ready.notify_one(); } /// 调度器主循环决策何时执行 Prefill 或 Decode /// /// 为什么不用固定轮询间隔 /// 固定间隔在高负载下延迟低低负载下浪费 CPU /// Notify timeout 结合既保证低延迟响应又避免忙等 pub async fn run_scheduler_loop(self) { loop { // 决策选择执行 Prefill 还是 Decode let decision self.make_scheduling_decision().await; match decision { SchedulingDecision::RunPrefill(requests) { self.execute_prefill_batch(requests).await; } SchedulingDecision::RunDecode { self.execute_decode_step().await; } SchedulingDecision::Idle { // 无任务可执行等待新请求 self.prefill_ready.notified().await; } } } } /// 调度决策核心逻辑 /// /// 为什么 Prefill 有更高优先级 /// 1. Prefill 延迟直接影响首 token 时间TTFT /// 2. Decode 延迟只影响 per-token 时间TPOT用户感知更弱 /// 3. Prefill 完成后才能开始 Decode延迟会级联传播 async fn make_scheduling_decision(self) - SchedulingDecision { let prefill_pending !self.prefill_queue.read().await.is_empty(); let decode_pending !self.decode_queue.read().await.is_empty(); if prefill_pending { // Prefill 优先级高于 Decode let mut queue self.prefill_queue.write().await; let batch_size queue.len().min(self.config.max_prefill_batch); let batch: Vec_ queue.drain(..batch_size).collect(); SchedulingDecision::RunPrefill(batch) } else if decode_pending { // 无 Prefill 时执行 Decode SchedulingDecision::RunDecode } else { SchedulingDecision::Idle } } /// 执行 Prefill 批次 /// /// Prefill 的执行策略 /// 1. 将同一批次的所有请求的 input tokens 拼接 /// 2. 一次 kernel launch 处理所有请求利用 GPU 的 batch 能力 /// 3. Prefill 完成后写入 KV Cache 并将请求移入 Decode 队列 async fn execute_prefill_batch(self, batch: VecInferenceRequest) { // 执行 Prefill调用推理引擎的实际计算函数 // 此处省略实际的 GPU kernel 调用 // Prefill 完成后将请求加入 Decode 队列 for mut req in batch { req.state RequestState::PendingDecode; self.decode_queue.write().await.push_back(req); } } /// 执行单步 Decode /// /// Decode 的执行策略Continuous Batching /// 1. 从 Decode 队列取尽可能多的请求受 decode_slots 限制 /// 2. 批量执行一步 Decode每个请求生成 1 个 token /// 3. 完成生成的请求移出未完成的回到队列 async fn execute_decode_step(self) { // 获取可用的 Decode 槽位 let available self.decode_slots.available_permits(); if available 0 { return; // 无可用槽位 } let mut queue self.decode_queue.write().await; let batch_size queue.len().min(available); if batch_size 0 { return; } let batch: Vec_ queue.drain(..batch_size).collect(); drop(queue); // 获取槽位许可 let permits: Vec_ (0..batch_size) .map(|_| self.decode_slots.clone().try_acquire_owned().unwrap()) .collect(); // 执行 Decode step // 完毕后归还槽位permits 在离开作用域时自动释放 // 处理结果 let mut completed Vec::new(); let mut continuing Vec::new(); for req in batch { if req.generated_count req.max_new_tokens { completed.push(req); } else { continuing.push(req); } } // 完成的请求移出未完成的重新入队 let mut queue self.decode_queue.write().await; queue.extend(continuing); // 释放槽位permits drop 时自动归还 Semaphore // 不需要显式操作——这就是 RAII 的价值 drop(permits); } } enum SchedulingDecision { RunPrefill(VecInferenceRequest), RunDecode, Idle, }流水线的延迟收益实测数据LLaMA-2-7B, A100, 32 并发模式TTFT (P50)TPOT (P50)GPU 利用率串行无流水线120ms25ms55%Prefill-Decode 分离85ms22ms78%TTFT 降低 29% 的主要原因是Prefill 不再需要等待所有 Decode 完成。在串行模式下当前请求的 Prefill 必须等前一个请求的所有 Decode 完毕。分离后Prefill 和 Decode 可以在不同时间片执行。更深层的工程挑战在于KV Cache 的一致性保证。当 Prefill 和 Decode 在不同 CUDA Stream 上执行时Prefill 写入的 KV Cache 必须对后续 Decode 可见。最简单的方案是在 Prefill 流和 Decode 流之间插入cudaStreamWaitEvent这会引入 ~2-5μs 的同步开销。更激进的做法是利用 CUDA 的 implicit stream ordering——当 Prefill 和 Decode 提交到同一个CUstream时CUDA 运行时自动保证顺序但牺牲了流的并行度。折中方案是将 KV Cache 按层分段Prefill 每完成一层就释放一个事件Decode 流在消费该层 KV Cache 前等待对应事件而非等待整个 Prefill 完成。这一层粒度的流水线化在 32 层的大模型上可额外降低 10%~15% 的 Decode 等待时间代价是需要更精细的 CUDA event 管理。在 Rust 侧实现时这些 CUDA 原语的封装需要特别注意cust库的Stream::wait_event是阻塞的在 Tokio 运行时中必须跑在专用线程上否则会阻塞整个 reactor 的事件循环。四、流水线调度的边界与资源限制KV Cache 的碎片化分离调度导致不同请求的 KV Cache 分配在时间上是交错的。随着请求完成和释放KV Cache 空间变成碎片。需要一个 KV Cache 管理器来分配和回收连续的显存空间——类似于操作系统的内存管理。Decode 槽位的饥饿如果 Prefill 请求持续涌入Decode 请求可能长时间得不到执行。需要引入最大连续 Prefill 次数限制强制在多个 Prefill 批次后执行一次 Decode。不适用场景纯离线批量推理无在线用户请求之间无时间交错需求极短 prompt 10 tokensPrefill 计算量太小分离调度的 overhead 超过收益单个大请求独占 GPU 的场景五、总结Prefill 和 Decode 阶段的资源需求特征互补计算 vs 访存分离调度可提升 GPU 利用率至 78%Prefill 优先于 Decode 的调度策略基于用户感知延迟模型——TTFT 比 TPOT 对体验影响更大Semaphore 控制 Decode 并发量确保显存KV Cache 占用不会超出硬件限制KV Cache 的碎片化和 Decode 饥饿是分离调度的两个主要工程挑战需要专门的管理机制分离调度最适合在线推理服务中的多用户并发场景离线批处理和极短 prompt 场景收益有限

相关新闻

3大突破!OpenFFBoard开源力反馈平台:从零到一打造专业级DIY模拟设备

3大突破!OpenFFBoard开源力反馈平台:从零到一打造专业级DIY模拟设备

3大突破!OpenFFBoard开源力反馈平台:从零到一打造专业级DIY模拟设备 【免费下载链接】OpenFFBoard OpenFFBoard is a universal force feedback interface for DIY simulation devices 项目地址: https://gitcode.com/gh_mirrors/op/OpenFFBoard …

2026/7/10 18:37:03 阅读更多 →
3步创建专属AI数字人:Duix.Avatar本地化部署终极指南

3步创建专属AI数字人:Duix.Avatar本地化部署终极指南

3步创建专属AI数字人:Duix.Avatar本地化部署终极指南 【免费下载链接】Duix-Avatar 🚀 Truly open-source AI avatar(digital human) toolkit for offline video generation and digital human cloning. 项目地址: https://gitcode.com/GitHub_Trendin…

2026/7/10 18:33:03 阅读更多 →
WebGoat语言切换终极指南:快速解决中文界面难题

WebGoat语言切换终极指南:快速解决中文界面难题

WebGoat语言切换终极指南:快速解决中文界面难题 【免费下载链接】WebGoat WebGoat is a deliberately insecure application 项目地址: https://gitcode.com/GitHub_Trending/we/WebGoat 你是否在使用WebGoat学习网络安全时,面对全英文界面感到困…

2026/7/10 18:33:03 阅读更多 →

最新新闻

实战选型指南:InsightFace_Pytorch与MobileFaceNet的人脸识别部署决策

实战选型指南:InsightFace_Pytorch与MobileFaceNet的人脸识别部署决策

实战选型指南:InsightFace_Pytorch与MobileFaceNet的人脸识别部署决策 【免费下载链接】InsightFace_Pytorch Pytorch0.4.1 codes for InsightFace 项目地址: https://gitcode.com/gh_mirrors/in/InsightFace_Pytorch 在人工智能落地浪潮中,人脸识…

2026/7/10 19:25:27 阅读更多 →
终极Mac硬件监控方案:Fanny如何通过通知中心实时掌控风扇转速与处理器温度

终极Mac硬件监控方案:Fanny如何通过通知中心实时掌控风扇转速与处理器温度

终极Mac硬件监控方案:Fanny如何通过通知中心实时掌控风扇转速与处理器温度 【免费下载链接】Fanny Monitor your Macs fan speed and CPU/GPU temperature from your Notification Center. 项目地址: https://gitcode.com/gh_mirrors/fa/Fanny Fanny是一款专…

2026/7/10 19:25:27 阅读更多 →
AD7175-8与STM32F303VE高精度数据采集方案解析

AD7175-8与STM32F303VE高精度数据采集方案解析

1. AD7175-8与STM32F303VE的黄金组合解析在工业测量和精密仪器领域,信号采集的精度和实时性往往决定着整个系统的性能上限。AD7175-8作为ADI公司推出的24位Σ-Δ型ADC,配合STM32F303VE这款自带硬件SPI和FPU的Cortex-M4 MCU,能够构建出超高精度…

2026/7/10 19:23:27 阅读更多 →
NBM5100A与STM32F415RG的纽扣电池电源管理方案

NBM5100A与STM32F415RG的纽扣电池电源管理方案

1. 项目背景与核心需求在物联网设备和便携式电子产品设计中,电池寿命和电流输出能力一直是工程师面临的两大挑战。以CR2032为代表的纽扣电池虽然体积小巧、便于集成,但其有限的容量和输出电流严重制约了设备的功能扩展和续航时间。传统方案往往需要在电池…

2026/7/10 19:23:26 阅读更多 →
为什么选择MacBERT?5个实战技巧提升中文NLP性能

为什么选择MacBERT?5个实战技巧提升中文NLP性能

为什么选择MacBERT?5个实战技巧提升中文NLP性能 【免费下载链接】MacBERT Revisiting Pre-trained Models for Chinese Natural Language Processing (MacBERT) 项目地址: https://gitcode.com/gh_mirrors/ma/MacBERT MacBERT中文预训练模型作为哈工大讯飞联…

2026/7/10 19:23:26 阅读更多 →
嵌入式条码扫描系统:LV3296与PIC18F46K80硬件架构与通信协议设计

嵌入式条码扫描系统:LV3296与PIC18F46K80硬件架构与通信协议设计

1. LV3296与PIC18F46K80硬件系统架构解析在嵌入式条码扫描系统中,LV3296作为核心采集模块,其内部集成了三个关键子系统:CMOS图像传感器、数字信号处理器(DSP)和通信接口控制器。这个组合实现了从物理光信号到数字信息的…

2026/7/10 19:19:26 阅读更多 →

日新闻

STM32与LTC1864高精度ADC的SPI通信实现

STM32与LTC1864高精度ADC的SPI通信实现

1. 项目背景与核心需求在工业控制和嵌入式系统开发中,模拟信号与数字系统的无缝集成一直是工程师面临的关键挑战。LTC1864作为一款16位高精度ADC转换器,配合STM32F101ZG这类主流微控制器,能够构建高性能的模拟信号采集系统。这种组合特别适合…

2026/7/10 0:03:07 阅读更多 →
猫抓插件:浏览器资源嗅探与视频下载的终极解决方案

猫抓插件:浏览器资源嗅探与视频下载的终极解决方案

猫抓插件:浏览器资源嗅探与视频下载的终极解决方案 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 还在为网页视频无法下载而烦恼吗&am…

2026/7/10 0:05:09 阅读更多 →
直流有刷电机驱动方案:TC78H653FTG与MKV46F256VLH16应用

直流有刷电机驱动方案:TC78H653FTG与MKV46F256VLH16应用

1. 直流有刷电机驱动方案概述在工业自动化和消费电子领域,直流有刷电机因其结构简单、控制方便、成本低廉等优势,仍然是许多应用场景的首选驱动方案。TC78H653FTG作为东芝推出的新一代H桥驱动器,与MKV46F256VLH16微控制器配合使用&#xff0c…

2026/7/10 0:05:09 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/10 19:03:29 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/9 13:46:46 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/9 21:41:05 阅读更多 →

月新闻