@@ -26,6 +26,7 @@ class HybridEngineRolloutConfig:
2626 """Configuration for HybridEngineRollout."""
2727 use_graph_capture : bool = False
2828 enable_profiling : bool = False
29+ use_shared_prefill : bool = False
2930
3031
3132class HybridEngineRollout (RolloutEngine ):
@@ -42,6 +43,7 @@ def __init__(self, engine, tokenizer, cfg=None):
4243 self .tokenizer = tokenizer
4344 self .use_graph_capture = getattr (cfg , 'use_graph_capture' , False ) if cfg else False
4445 self .enable_profiling = getattr (cfg , 'enable_profiling' , False ) if cfg else False
46+ self .use_shared_prefill = getattr (cfg , 'use_shared_prefill' , False ) if cfg else False
4547 self ._last_profile = None
4648
4749 @torch .no_grad ()
@@ -77,20 +79,31 @@ def generate(self, request: RolloutRequest, sampling: SamplingConfig) -> Rollout
7779
7880 is_greedy = sampling .temperature <= 0.0
7981
80- if self .use_graph_capture and is_greedy :
81- output_ids = self ._generate_graph (prompt_ids , prompt_attn , max_new_tokens , pad_token_id , module , device )
82- else :
83- temperature = max (sampling .temperature , 1e-8 )
84- do_sample = not is_greedy
85- output_ids = module .generate (
86- prompt_ids ,
87- attention_mask = prompt_attn ,
88- max_new_tokens = max_new_tokens ,
89- do_sample = do_sample ,
90- temperature = temperature if do_sample else 1.0 ,
91- top_p = sampling .top_p if do_sample else 1.0 ,
92- pad_token_id = pad_token_id ,
93- )
82+ shared_prefill_handles = []
83+ if self .use_shared_prefill and n > 1 :
84+ if self .use_graph_capture :
85+ raise RuntimeError ("Shared prefill does not support CUDA graph capture" )
86+ self .engine .prepare_shared_prefill (B , n , prompt_len )
87+ shared_prefill_handles = self ._register_shared_prefill_hooks (module , B , n )
88+ try :
89+ if self .use_graph_capture and is_greedy :
90+ output_ids = self ._generate_graph (prompt_ids , prompt_attn , max_new_tokens , pad_token_id , module ,
91+ device )
92+ else :
93+ temperature = max (sampling .temperature , 1e-8 )
94+ do_sample = not is_greedy
95+ output_ids = module .generate (
96+ prompt_ids ,
97+ attention_mask = prompt_attn ,
98+ max_new_tokens = max_new_tokens ,
99+ do_sample = do_sample ,
100+ temperature = temperature if do_sample else 1.0 ,
101+ top_p = sampling .top_p if do_sample else 1.0 ,
102+ pad_token_id = pad_token_id ,
103+ )
104+ finally :
105+ for handle in shared_prefill_handles :
106+ handle .remove ()
94107
95108 if self .enable_profiling :
96109 accelerator .synchronize ()
@@ -141,6 +154,43 @@ def get_last_profile(self):
141154 """Return the most recent profiling snapshot for this rollout instance."""
142155 return self ._last_profile
143156
157+ def _register_shared_prefill_hooks (self , module , batch_size , repeats ):
158+ state = {"pending" : True , "reduced" : False }
159+
160+ def reduce_prompt_batch (_module , args , kwargs ):
161+ input_ids = kwargs .get ("input_ids" )
162+ if not state ["pending" ]:
163+ return args , kwargs
164+ if input_ids is None :
165+ raise RuntimeError ("Shared prefill requires input_ids as a keyword argument" )
166+ expected_batch_size = batch_size * repeats
167+ if input_ids .shape [0 ] != expected_batch_size :
168+ raise RuntimeError ("Shared prefill input batch does not match the expanded rollout batch" )
169+ if input_ids .shape [1 ] <= 1 :
170+ raise RuntimeError ("Shared prefill requires a prompt with more than one token" )
171+ kwargs = dict (kwargs )
172+ kwargs ["input_ids" ] = input_ids [::repeats ]
173+ for name in ("attention_mask" , "position_ids" , "token_type_ids" ):
174+ value = kwargs .get (name )
175+ if isinstance (value , torch .Tensor ) and value .shape [0 ] == expected_batch_size :
176+ kwargs [name ] = value [::repeats ]
177+ state ["reduced" ] = True
178+ return args , kwargs
179+
180+ def expand_prompt_output (_module , _args , _kwargs , output ):
181+ if not state ["pending" ]:
182+ return output
183+ if not state ["reduced" ]:
184+ raise RuntimeError ("Shared prefill did not reduce the prompt batch" )
185+ state ["pending" ] = False
186+ output .past_key_values = self .engine .repeat_shared_prefill_cache (batch_size , repeats )
187+ output .logits = output .logits .repeat_interleave (repeats , dim = 0 )
188+ return output
189+
190+ pre_handle = module .register_forward_pre_hook (reduce_prompt_batch , with_kwargs = True )
191+ post_handle = module .register_forward_hook (expand_prompt_output , with_kwargs = True )
192+ return pre_handle , post_handle
193+
144194 # ------------------------------------------------------------------
145195 # Graph capture decode loop (greedy only)
146196 # ------------------------------------------------------------------
0 commit comments