@@ -71,11 +71,6 @@ def __init__(
7171 if self ._stream is not None :
7272 logger .debug_once ("Enabled separate cuda stream for MoE shared_experts" )
7373
74- if self ._stream is not None :
75- # One pair per DBO ubatch id.
76- self ._input_ready_event = [torch .cuda .Event (), torch .cuda .Event ()]
77- self ._output_ready_event = [torch .cuda .Event (), torch .cuda .Event ()]
78-
7974 # TODO(bnell): Hack for elastic_ep. Get rid of this
8075 def _set_moe_config (self , new_moe_config : FusedMoEConfig ):
8176 self .moe_config = new_moe_config
@@ -101,14 +96,6 @@ def _disable_shared_experts_overlap(self) -> bool:
10196 and parallel_config .all2all_backend not in _EPLB_OVERLAP_SAFE_BACKENDS
10297 ) or parallel_config .use_fi_nvl_two_sided_kernels
10398
104- @property
105- def _should_enable_stream_overlap_heuristic (self ) -> bool :
106- # On ROCm, empirically it's shown that only DPA deployments benefit from
107- # multi-stream shared experts
108- if not current_platform .is_rocm ():
109- return True
110- return self ._moe_config .moe_parallel_config .dp_size > 1
111-
11299 def _determine_shared_experts_order (
113100 self ,
114101 hidden_states : torch .Tensor ,
@@ -120,43 +107,49 @@ def _determine_shared_experts_order(
120107 return SharedExpertsOrder .MK_INTERNAL_OVERLAPPED
121108
122109 should_run_shared_in_aux_stream = (
123- current_platform .is_cuda_alike ()
110+ current_platform .is_cuda ()
124111 and self ._stream is not None
125112 and hidden_states .shape [0 ]
126113 <= envs .VLLM_SHARED_EXPERTS_STREAM_TOKEN_THRESHOLD
127- and self ._should_enable_stream_overlap_heuristic
128114 )
129115
130116 if should_run_shared_in_aux_stream :
131117 return SharedExpertsOrder .MULTI_STREAM_OVERLAPPED
132118 else :
133119 return SharedExpertsOrder .NO_OVERLAP
134120
135- def maybe_forward_async (self , shared_experts_input : torch .Tensor ) -> bool :
136- """Enqueue shared experts on the aux stream without waiting for them.
137-
138- Returns true if the shared experts were enqueued, false otherwise. Call
139- `wait` to wait for the shared experts to finish if this returns true.
140- """
141- if (
142- self ._determine_shared_experts_order (shared_experts_input )
143- != SharedExpertsOrder .MULTI_STREAM_OVERLAPPED
144- ):
145- return False
146- assert self ._stream is not None
147- idx = self ._output_idx
148- assert self ._output [idx ] is None
149- self ._input_ready_event [idx ].record (current_stream ())
121+ def maybe_sync_shared_experts_stream (
122+ self ,
123+ shared_experts_input : torch .Tensor ,
124+ ):
125+ experts_order = self ._determine_shared_experts_order (shared_experts_input )
126+
127+ if experts_order == SharedExpertsOrder .MULTI_STREAM_OVERLAPPED :
128+ assert self ._stream is not None
129+
130+ # Record that the clone will be used by shared_experts_stream
131+ # to avoid gc issue from deallocation of hidden_states_clone
132+ # For more details: https://docs.pytorch.org/docs/stable/generated/torch.Tensor.record_stream.html # noqa: E501
133+ # NOTE: We don't need shared_output.record_stream(current_stream())
134+ # because we synch the streams before using shared_output.
135+ shared_experts_input .record_stream (self ._stream )
136+
137+ # Mark sync start point for the aux stream since we will
138+ # run in parallel with router/gate.
139+ self ._stream .wait_stream (current_stream ())
140+
141+ def _run_in_aux_stream (
142+ self ,
143+ shared_experts_input : torch .Tensor ,
144+ ) -> torch .Tensor :
145+ # TODO: assert that maybe_sync_shared_experts_stream has been called.
146+
147+ # Run shared experts in parallel on a separate stream.
150148 with torch .cuda .stream (self ._stream ):
151- self ._input_ready_event [idx ].wait (self ._stream )
152- self ._output [idx ] = self ._layer (shared_experts_input )
153- self ._output_ready_event [idx ].record (self ._stream )
154- return True
149+ output = self ._layer (shared_experts_input )
150+ current_stream ().wait_stream (self ._stream )
155151
156- def wait (self ) -> None :
157- """Block the main stream until `maybe_forward_async` output is ready."""
158- assert self ._stream is not None
159- self ._output_ready_event [self ._output_idx ].wait (current_stream ())
152+ return output
160153
161154 @property
162155 def _output_idx (self ) -> int :
@@ -181,6 +174,11 @@ def forward(
181174
182175 assert self ._output [self ._output_idx ] is None
183176
184- self ._output [self ._output_idx ] = self ._layer (shared_experts_input )
177+ if order == SharedExpertsOrder .MULTI_STREAM_OVERLAPPED :
178+ self ._output [self ._output_idx ] = self ._run_in_aux_stream (
179+ shared_experts_input
180+ )
181+ else :
182+ self ._output [self ._output_idx ] = self ._layer (shared_experts_input )
185183
186184 assert self ._output [self ._output_idx ] is not None
0 commit comments