
大規模強化學習訓練階段模型不是只跑訓練迭代還要同時做大量 Rollout 生成。每個訓練 step 都可能觸發成百上千條推理請求這些請求混合了短回復、長生成、多輪對話和獎勵模型打分調度起來和傳統在線推理服務完全不是一回事。Prefix Locality 是這幾年 KV Cache 優化里一個很有效的思路但在混合 RL Rollout 場景下只靠前綴復用并不能解決所有問題。這篇文章我們不聊概念直接拆解“Scheduling Mixed RL Rollouts Beyond Prefix Locality”背后的調度問題講清楚為什么前綴局部性不夠用、混合 Rollout 到底混合了什么、調度器應該怎么重新設計以及落地時該看哪些指標。如果你正在做 RL 訓練框架、推理引擎選型或者要給大模型訓練集群設計調度模塊這篇文章可以直接收藏。全文會覆蓋核心機制拆解、調度器設計路徑、配置示例、評測方法和常見坑位避免你在實際搭建時走彎路。1. 核心概念速覽能力項說明主題類型大規模 RL 訓練中的 Rollout 推理調度策略設計核心問題打破 Prefix Locality 的單一優化視角處理混合請求、動態前綴和訓練-推理資源競爭關鍵技術動態前綴索引、優先級調度、兩階段資源預算、冷啟動調度策略、訓練感知調度適用場景LLM RL 訓練管道、分布式推理引擎、在線 Rollout 服務、多任務混合推理隊列主要收益提升樣本吞吐、降低訓練空轉、提高 KV Cache 復用率、平滑訓練早期冷啟動波動不適用場景單模型單請求的簡單推理、無 RL 訓練的純在線對話服務落地難度中高涉及調度器、緩存索引、訓練器多模塊協同先回到最基礎的問題Prefix Locality 是什么它利用了“多個請求共享同一段前綴”的特征在推理時只計算一次共享前綴的 KV Cache后續請求直接復用。這在共享系統提示詞、Few-shot 示例、多輪對話歷史等場景下非常有效。vLLM 的 prefix caching、SGLang 的 RadixAttention 都是這個思路的代表實現。但 RL Rollout 和傳統在線推理有一個本質差別請求分布不是靜態的。策略網絡每輪都在更新生成結果越來越偏離初始前綴分布同時 Rollout 請求還混著獎勵模型打分、短回答、長生成等不同類型。單純按前綴組織調度容易出現前綴樹碎片化、緩存命中率忽高忽低、訓練器餓死等問題。下面我們把 Prefix Locality 的適用邊界和失效場景拆開來看。2. 為什么 Prefix Locality 在 RL Rollout 場景中不夠用2.1 在線推理與 RL Rollout 的差異在線推理服務里請求來源是用戶前綴結構相對穩定。比如同一產品的所有用戶都共享系統提示詞或者同一個會話的后續輪次復用之前的歷史。這種情況下前綴復用收益穩定且可預測調度器可以放心地把緩存命中率當作核心優化項。RL Rollout 的場景完全不同第一產生請求的主體是訓練器。每個訓練 step 之后策略網絡的參數變了導致后續請求的內容分布發生變化。雖然 prompt 本身可能來自同一個任務族但策略更新后生成的響應、采樣的動作、獎勵模型打分的對象都會漂移。第二請求類型是混合的。同一個調度周期內可能同時存在需要生成 512 個 token 的長回答只需要輸出“0/1”或一個分數的獎勵查詢需要多輪交互的狀態查詢從回放緩沖區里采樣的舊策略數據重放。這些請求對前綴的敏感程度、對延遲的要求、對資源的消耗完全不同。如果一個調度器只盯前綴復用很容易把資源全部喂給了長序列復用請求導致短請求和關鍵路徑上的訓練請求餓死。第三訓練與推理共享顯存和算力。RL 訓練環節GPU 既要跑訓練的前向反向又要跑 Rollout 的推理生成還要跑獎勵模型的打分。調度器如果不感知訓練器的當前狀態就會出現推理請求把算力占滿、訓練 step 遲遲等不到 Rollout 數據的情況。2.2 Prefix Locality 失效的具體場景場景一請求前綴高度動態。當任務本身沒有穩定共享前綴時比如每個 prompt 都是獨立采樣的數學題、代碼題前綴復用率天然很低。此時 Prefix Locality 的收益很小繼續按前綴聚合請求反而增加調度器的組織成本。場景二前綴樹碎片化。RL 訓練里同一個任務族往往基于一個基礎 prompt 做少量擾動。這些 prompt 之間共享很長一段前綴但結尾略有差異。如果調度器把每條請求都當作獨立前綴插入Radix Tree 會越來越碎索引本身占用內存緩存命中率卻沒有提升。需要定期合并、剪枝和過期清理。場景三訓練冷啟動階段。訓練初期策略網絡接近隨機初始化生成的輸出五花八門前綴復用率極低。此時如果調度器執著于“找到可復用的前綴塊”反而會延遲請求執行。材料里提到的“rl 冷啟動”就是這個問題冷啟動時模型輸出熵高、分布不穩定調度策略應該偏重快速收集多樣樣本而不是追求緩存復用等訓練中后期策略收斂、輸出分布穩定后再逐步提高前綴復用權重。場景四延遲敏感的訓練關鍵路徑。RL 訓練中Rollout 數據是訓練 step 的“原料”。如果調度器把所有請求都按最大吞吐模式排布某個關鍵 batch 的 Rollout 結果遲遲不返回訓練器只能空轉。調度器需要區分“關鍵路徑請求”和“后臺請求”前者優先后者可以等待。所以超越 Prefix Locality 的核心不是拋棄前綴復用而是把它從“唯一目標”降級為“一個加權因素”同時引入訓練感知、多樣性保障和混合請求優先級。3. Mixed RL Rollouts 到底混合了什么設計調度策略之前先把“混合”這個詞拆清楚。混合 RL Rollout 至少在三個維度上混合。3.1 任務類型混合請求類型輸出長度計算特征典型用途生成式 Rollout中到長128~1024 token高計算量KV Cache 增長快策略采樣、軌跡生成判別式/獎勵查詢極短1~10 token低計算量延遲敏感獎勵模型打分、價值估計狀態/動作查詢短到中中等計算量多輪決策、環境交互回放數據重放任意無在線生成只做訓練輸入舊策略樣本訓練不同任務類型的資源消耗差異極大。一個生成式請求的 token 消耗可能是獎勵查詢的百倍。調度器必須按類型拆分隊列否則一個長生成請求會阻塞后面大量短請求。3.2 數據分布混合RL 訓練過程中Rollout 數據來自不同階段當前策略采樣舊策略的 replay buffer混有探索噪聲的樣本來自不同任務群體的數據。這些數據的前綴分布不同冗余程度也不同。調度器如果只按請求到達順序處理可能出現某個任務族的樣本嚴重過采樣另一個任務族長期饑餓導致訓練數據分布偏移。3.3 資源需求混合訓練機器上GPU 資源不是全部分給推理的。訓練 step 和 Rollout 生成存在時間上的交錯訓練前向/反向階段計算資源被訓練占用Rollout 階段訓練器等待數據資源釋放給推理。調度器需要做資源預算控制明確“當前時間片里推理最多占多少顯存和算力”。否則訓練和推理會出現互相爭搶吞吐反而低于串行執行。3.4 混合的意義“Mixed”是問題同時也是優化機會。不同類型的請求可以互相填補調度空隙。例如短請求可以插在長請求的 Prefill 階段中間執行緩存復用率高的請求可以優先填充等待窗口。調度器的核心設計目標就是在這些混合請求之間找到一組調度順序和資源分配策略使得訓練樣本吞吐最大化、訓練空轉時間最小化。4. 調度器核心設計思路超越 Prefix Locality4.1 設計目標調度器不再追求單一指標例如緩存命中率而是維護一個綜合目標調度收益 前綴復用收益 訓練關鍵路徑緊迫度 數據多樣性貢獻 - 執行維護成本。每一項都可以配置權重權重隨訓練階段動態調整。4.2 動態前綴索引與老化機制Prefix Locality 仍然是重要工具但需要做三件事第一前綴索引支持動態過期。RL 訓練中策略更新后舊策略生成的前綴不再有復用價值應該從索引中淘汰。可以記錄每個前綴塊的“最近命中時間”和“策略版本號”當策略版本落后超過閾值時直接標記失效。第二前綴合并與剪枝。對于差異極小的 prompt可以對前綴樹做自動合并減少碎片化。相似度計算可以用公共前綴長度、token 編輯距離等策略。第三分階段調整復用權重。訓練早期前綴復用率低調度器降低復用權重優先保證請求快速執行訓練中后期策略收斂輸出分布穩定逐步提高復用權重。4.3 混合請求分類與優先級調度器把請求分為三類Critical Request當前訓練 step 依賴的 Rollout 請求必須盡快完成Reusable Request前綴命中率高、可以延遲執行換取緩存復用的請求Background Request回放數據、評估數據、日志生成等后臺任務可以在資源空閑時執行。調度優先級按 Critical Reusable Background 處理同時配合時間片限制防止高優請求無限占用資源。4.4 兩階段調度資源預算分配 執行順序編排一個大致的實現思路是兩階段階段一預算分配。根據訓練器當前狀態是否在 waiting、下一個 step 需要多少樣本決定當前調度窗口內推理最多消耗多少顯存和算力哪些任務配額多少。階段二執行順序。在預算約束內對就緒請求排隊按優先級、前綴復用率、預計執行時間進行排序選擇當前批次的最優組合。這種兩階段解耦的好處是資源控制與緩存優化互不影響訓練器側只需要關心預算接口調度器內部可以自由調整執行策略。4.5 冷啟動調度策略對應“rl 冷啟動”問題調度器需要在訓練初期采用不同參數調度參數冷啟動階段穩定訓練階段前綴復用權重低高批量請求大小小快速出結果大追求吞吐多樣性采樣權重高覆蓋任務分布中按需調整緩存過期時限短舊前綴盡快清理長保留穩定前綴關鍵路徑放松度高優先喂數據給訓練器中平衡吞吐冷啟動階段的目標是“快速產生多樣且有效的 Rollout 數據”讓訓練器盡快收斂到一個分布更穩定的策略上。等策略穩定后再切換到高復用、高吞吐模式。5. 調度器核心模塊與實現路徑這里給出一個簡化的調度器模塊劃分與實現思路。注意下面代碼是設計示例不是某個特定開源項目的完整實現落地時需要結合你的推理框架和訓練框架接口調整。5.1 模塊劃分整體可以拆成五個核心模塊請求接收器接收訓練器、回放緩沖區、評估任務發來的請求統一封裝為RLRequest。前綴索引器維護動態前綴樹支持插入、命中、過期、剪枝。預算控制器從訓練器獲取資源狀態計算調度窗口的 GPU 資源預算。優先級隊列按分類和優先級組織就緒請求。執行器與推理引擎交互提交 batch收集結果。5.2 請求封裝與優先級打分代碼示例# 請求封裝示例 import time from dataclasses import dataclass, field dataclass class RLRequest: req_id: str prompt_tokens: list task_type: str # generation / reward / state_query / replay max_tokens: int arrival_ts: float field(default_factorytime.time) is_critical: bool False # 當前訓練 step 是否依賴 policy_version: int 0 # 生成該請求的策略版本 prefix_hit_len: int 0 # 調度時動態填充 python # 優先級打分示例 def score_request(req: RLRequest, weights: dict) - float: prefix_score req.prefix_hit_len / max(len(req.prompt_tokens), 1) critical_score 1.0 if req.is_critical else 0.0 waiting_score min((time.time() - req.arrival_ts) / 60.0, 1.0) score ( weights[prefix] * prefix_score weights[critical] * critical_score weights[waiting] * waiting_score ) return score5.3 前綴索引示例實際前綴樹實現比較復雜這里給一個基于字典的簡化版本便于理解核心邏輯# 簡化前綴索引示例 class PrefixIndex: def __init__(self, expiry_version_threshold: int 3): self.trie {} self.expiry_threshold expiry_version_threshold def insert(self, token_ids: list, policy_version: int): node self.trie for tid in token_ids: if tid not in node: node[tid] {children: {}, version: policy_version, hits: 0} node node[tid][children] node[_end] {version: policy_version, hits: 0} def match(self, token_ids: list, current_version: int) - int: node self.trie hit_len 0 for tid in token_ids: if tid not in node: break node node[tid][children] if node.get(_end) is None: # 非完整前綴按需判斷是否記錄 hit_len 1 elif current_version - node[_end][version] self.expiry_threshold: node[_end][hits] 1 hit_len 1 else: break return hit_len生產環境中建議使用 Radix Tree 或類似 SGLang RadixAttention 的 Prefix Cache并加上并發鎖和異步清理策略。5.4 調度主循環示例# 調度主循環偽代碼 def scheduling_loop(ready_queue, prefix_index, budget_controller, executor): while True: requests ready_queue.get_ready_batch(max_batchbudget_controller.current_batch_size()) for req in requests: req.prefix_hit_len prefix_index.match(req.prompt_tokens, req.policy_version) requests.sort(keylambda r: score_request(r, current_weights()), reverseTrue) selected budget_controller.filter_by_resource_budget(requests) if selected: executor.submit(selected) for req in selected: prefix_index.insert(req.prompt_tokens, req.policy_version) budget_controller.release_waiting_resources() time.sleep(0.01)核心要點是排序前先做前綴匹配排序混合多因子提交前再做資源預算過濾。這樣可以在不犧牲資源控制的前提下吸收 Prefix Locality 的部分收益。6. 接口與配置設計調度器通常通過 gRPC 或 HTTP 與訓練器交互。下面給出一個通用的配置和請求示例落地時按項目調整。6.1 調度器配置示例# scheduler_config.yaml 示例 scheduler: max_batch_size: 32 scheduling_interval_ms: 10 queue_capacity: 4096 weights: prefix: 0.4 critical: 0.4 waiting: 0.2 policy: cold_start_steps: 200 # 訓練前 200 步視為冷啟動 cold_start_prefix_weight: 0.1 # 冷啟動時降低前綴復用權重 stable_prefix_weight: 0.4 # 穩定階段恢復 prefix_ttl_steps: 5 # 前綴塊策略版本過期閾值 budget: max_gpu_memory_mb: 40960 training_reserved_mb: 20480 # 為訓練保留的顯存 max_inference_mb: 20480 # 推理可用顯存上限 executor: engine: vllm # 也可以是 sglang / tensorrt-llm timeout_seconds: 1206.2 訓練器提交 Rollout 請求示例import requests payload { task_type: generation, prompt: Solve the following math problem step by step., max_tokens: 512, is_critical: True, policy_version: 12, } resp requests.post(http://127.0.0.1:8900/submit_rollout, jsonpayload, timeout10) print(resp.json())6.3 調度器返回結果示例{ req_id: rl_20250101_00001, status: accepted, estimated_batch: batch_20250101_0003, queue_position: 2, prefix_match_length: 128 }實際生產環境建議用 gRPC 流式接口支持長連接和批量結果返回避免 HTTP 短連接在大規模 RL 訓練下成為瓶頸。7. 評測方法與性能觀察超越 Prefix Locality 的效果不能只看緩存命中率需要一套針對 RL 訓練場景的評測指標。7.1 核心指標指標含義目標Sample Throughput每秒產出有效 Rollout 樣本數越高越好GPU Idle Ratio訓練器等待 Rollout 數據的時間占比越低越好Prefix Hit RatioKV Cache 前綴命中比例輔助指標不是唯一目標Training Step Time單次訓練 step 總耗時越低越好Diversity Score生成樣本的分布覆蓋度冷啟動階段重點觀察Cache Memory Overhead前綴索引和緩存占用額外內存控制合理范圍7.2 評測方法推薦做 A/B 對比Baseline 1純 Prefix Locality 調度按前綴命中優先Baseline 2純 FIFO 調度不做特殊優化Test混合調度前綴復用 關鍵路徑 冷啟動策略。每個方案固定相同的訓練數據、模型和訓練超參數跑相同的訓練步數對比總耗時、樣本吞吐、GPU 空閑時間和最終模型效果。評測腳本示例# 評測調度策略對比示例 import time import statistics def run_training_steps(scheduler_type, steps100): start time.time() sample_throughputs [] for step in range(steps): t0 time.time() rollout_data scheduler_collect(scheduler_type, step) train_one_step(rollout_data) cost time.time() - t0 sample_throughputs.append(len(rollout_data) / cost) total_time time.time() - start return { total_time: total_time, avg_throughput: statistics.mean(sample_throughputs), gpu_idle_ratio: compute_gpu_idle_ratio(), prefix_hit_ratio: compute_prefix_hit_ratio(), }7.3 性能觀察重點實際運行時要重點觀察三個位置第一訓練器側等待日志。如果訓練 step 的日志里頻繁出現waiting for rollout data說明調度器沒有優先保障關鍵路徑請求。第二GPU 顯存分配趨勢。訓練和推理的顯存分配是否在交替攀升還是長期互相擠壓。可以通過nvidia-smi定時采樣觀察顯存水位。第三前綴索引的內存開銷。當請求量達到百萬級時前綴索引本身可能占用數 GB 內存。如果發現索引內存異常增長需要檢查過期清理策略是否生效。8. 資源占用與性能權衡8.1 顯存開銷前綴 Cache 是提升復用率的重要手段但會占用顯存。前綴索引本身存放在 CPU 內存時還需要考慮 CPU 內存和 GPU 顯存之間的搬運開銷。經驗上生產環境會限制前綴 Cache 的最大顯存容量。比如設置一個max_cache_gpu_mb超過后觸發 LRU 淘汰。調度器應該把緩存淘汰策略暴露為配置項而不是寫死。8.2 CPU 調度開銷調度器本身是一個高頻決策模塊。每 10ms 做一次批次決策時如果請求隊列非常大排序成本不可忽視。優化方向使用堆隊列代替全量排序對相同前綴的請求預先分組減少排序規模調度決策異步化不阻塞請求接收。8.3 長請求與短請求的互相影響一個長生成請求如果占滿了執行器的 batch 窗口后臺短請求就會堆積。一個常見做法是時間片輪轉執行器每執行 N 個長請求后強制插入一批短請求。這比純粹按優先級排序更穩定。8.4 權衡總結權衡點偏向 Prefix 復用偏向訓練吞吐長處節省重復計算提升推理效率保證訓練器持續有數據風險訓練器等待、多樣性下降計算重疊增加、KV 緩存利用率下降適用階段策略穩定、前綴穩定冷啟動、策略頻繁更新、任務多樣調度器的參數不應該是一次性固定的而應該隨訓練狀態動態調整。例如用訓練步數、策略更新頻率、最近 N 步 Rollout 數據的平均熵作為狀態信號。9. 常見問題與排查方法問題現象可能原因排查方式解決方案訓練 step 頻繁等待 Rollout 數據關鍵路徑請求優先級不足查看調度日志中 critical 請求的排隊時間提高 critical 權重或為關鍵路徑單獨開隊列前綴命中率很高但訓練速度反而下降過度追求復用犧牲了樣本多樣性對比命中率和樣本多樣性評分降低 prefix 權重增加多樣性采樣冷啟動階段 Rollout 輸出無效樣本多策略未收斂調度器未切換冷啟動模式觀察策略熵變化和輸出分布冷啟動階段降低復用權重、縮短緩存過期時間前綴索引占內存過大過期前綴未及時清理檢查策略版本淘汰日志縮短 prefix_ttl_steps定期合并前綴樹訓練與推理顯存互相擠壓資源預算沒有生效查看 budget 配置和顯存采樣曲線設置訓練保留顯存控制推理最大顯存長請求阻塞短請求批次調度沒有時間片輪轉查看短請求的排隊時延增加時間片輪轉策略定期強制插入短請求GPU 利用率波動大調度窗口和訓練步調不匹配對比調度間隔與訓練 step 時間調整 scheduling_interval_ms對齊訓練步調回放數據與在線數據比例失衡后臺請求優先級過高查看不同任務類型樣本占比降低 Background 請求權重限制回放請求配額10. 最佳實踐與落地建議第一先跑通最小閉環再上規模。建議先用一個小模型、小顯存環境把“訓練器 - 調度器 - 推理引擎 - 結果回填”這條鏈路跑通驗證調度器不會成為新的瓶頸再逐步擴大請求量和 batch size。第二調度參數不要拍腦袋。每次調整 Prefix 權重、Critical 權重后至少跑 50~100 個訓練 step對比樣本吞吐和訓練總耗時。第三指標日志要打全。訓練器側至少記錄每個 step 的等待時間調度器側記錄請求排隊時長、每個 batch 的執行時間、前綴命中分布執行器側記錄 KV Cache 命中率、顯存占用峰值。三者拼起來才能定位問題。第四冷啟動和穩定階段分開設計。不要在訓練一開始就開滿所有優化容易讓調度器在異常分布上自我消耗。建議階段切換使用訓練步數和策略熵雙閾值觸發。第五合規與數據安全。RL 訓練中涉及的用戶數據、生成內容、獎勵模型標注數據要確保來源合法、授權清晰。涉及人臉、聲音、版權素材的生成任務必須確認授權邊界模型進入生產或商用前要對邏輯漏洞、偏見和有害內容做額外評估。調度器相關實驗建議在隔離的開發環境進行并保留完整的訓練與推理日志方便追溯。第六注意開源許可。引用或集成 vLLM、SGLang、Ray、訓練框架等三方組件時檢查各自的 License 是否滿足你的分發要求。不要因為調度器的代碼只是薄薄一層就忽略底層框架的許可約束。11. 總結與下一步這個方向最值得嘗試的點在于把調度器從“KV Cache 的工具人”升級為 RL 訓練管道的核心組件。具體來說就是先定義清楚訓練目標再把前綴復用、關鍵路徑、數據多樣性、冷啟動策略加權融合到一個可配置的調度器里。它解決的問題是真實存在的大規模 RL 訓練中Rollout 請求混雜、前綴漂移、訓練推理爭資源這些問題只靠傳統 Prefix Locality 無法根除。最先應該驗證的功能是兩階段調度中的預算控制。因為如果訓練和推理的資源邊界劃不清楚后面所有優化都會建立在沙地上。最容易踩的坑是照著在線推理服務的思路做調度把緩存命中率當作 KPI忽略訓練器的實時狀態。結果是緩存看起來很漂亮訓練吞吐卻上不去。后續可以繼續擴展的方向包括基于強化學習的自適應調度參數調整、多機多卡環境下的跨節點前綴共享、以及調度器與訓練器聯合編排的端到端 pipeline。這里的關鍵是“先設計訓練感知的調度接口再逐步加緩存優化”不要把順序搞反了。