Skip to content

Commit fe468f9

Browse files
Add pipelining for Qwen 3.5 (ml-explore#1345)
* Add pipelining to qwen 3.5 * Fix sharded_load --------- Co-authored-by: Anastasiia Filippova <a_filippova@apple.com>
1 parent 2edfb81 commit fe468f9

1 file changed

Lines changed: 52 additions & 7 deletions

File tree

mlx_lm/models/qwen3_5.py

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
)
1616
from .cache import ArraysCache, KVCache
1717
from .gated_delta import gated_delta_update
18+
from .pipeline import PipelineMixin
1819
from .qwen3_next import Qwen3NextAttention as Attention
1920
from .qwen3_next import Qwen3NextMLP as MLP
2021
from .qwen3_next import Qwen3NextRMSNormGated as RMSNormGated
@@ -240,7 +241,7 @@ def __call__(
240241
return out
241242

242243

243-
class Qwen3_5TextModel(nn.Module):
244+
class Qwen3_5TextModel(PipelineMixin, nn.Module):
244245
def __init__(self, args: TextModelArgs):
245246
super().__init__()
246247
self.embed_tokens = nn.Embedding(args.vocab_size, args.hidden_size)
@@ -251,6 +252,18 @@ def __init__(self, args: TextModelArgs):
251252
self.ssm_idx = 0
252253
self.fa_idx = args.full_attention_interval - 1
253254

255+
def pipeline(self, group):
256+
super().pipeline(group)
257+
self.ssm_idx = None
258+
self.fa_idx = None
259+
for e, l in enumerate(self.pipeline_layers):
260+
if self.ssm_idx is None and l.is_linear:
261+
self.ssm_idx = e
262+
elif self.fa_idx is None and not l.is_linear:
263+
self.fa_idx = e
264+
if self.ssm_idx is not None and self.fa_idx is not None:
265+
break
266+
254267
def __call__(
255268
self,
256269
inputs: mx.array,
@@ -262,16 +275,44 @@ def __call__(
262275
else:
263276
hidden_states = self.embed_tokens(inputs)
264277

278+
pipeline_rank = self.pipeline_rank
279+
pipeline_size = self.pipeline_size
280+
265281
if cache is None:
266-
cache = [None] * len(self.layers)
282+
cache = [None] * len(self.pipeline_layers)
267283

268-
fa_mask = create_attention_mask(hidden_states, cache[self.fa_idx])
269-
ssm_mask = create_ssm_mask(hidden_states, cache[self.ssm_idx])
284+
fa_mask = None
285+
ssm_mask = None
286+
if self.fa_idx is not None:
287+
fa_mask = create_attention_mask(hidden_states, cache[self.fa_idx])
288+
if self.ssm_idx is not None:
289+
ssm_mask = create_ssm_mask(hidden_states, cache[self.ssm_idx])
270290

271-
for layer, c in zip(self.layers, cache):
291+
# Receive from the previous process in the pipeline
292+
if pipeline_rank < pipeline_size - 1:
293+
hidden_states = mx.distributed.recv_like(hidden_states, (pipeline_rank + 1))
294+
295+
for layer, c in zip(self.pipeline_layers, cache):
272296
mask = ssm_mask if layer.is_linear else fa_mask
273297
hidden_states = layer(hidden_states, mask=mask, cache=c)
274298

299+
# Send to the next process in the pipeline
300+
if pipeline_rank != 0:
301+
hidden_states = mx.distributed.send(
302+
hidden_states, (pipeline_rank - 1) % pipeline_size
303+
)
304+
if cache[-1] is not None:
305+
if hasattr(cache[-1], "keys"):
306+
cache[-1].keys = mx.depends(cache[-1].keys, hidden_states)
307+
else:
308+
cache[-1][0] = mx.depends(cache[-1][0], hidden_states)
309+
310+
# Broadcast h while keeping it in the graph
311+
if pipeline_size > 1:
312+
hidden_states = mx.distributed.all_gather(hidden_states)[
313+
: hidden_states.shape[0]
314+
]
315+
275316
return self.norm(hidden_states)
276317

277318

@@ -299,7 +340,7 @@ def __call__(
299340

300341
@property
301342
def layers(self):
302-
return self.model.layers
343+
return self.model.pipeline_layers
303344

304345
def make_cache(self):
305346
return [ArraysCache(size=2) if l.is_linear else KVCache() for l in self.layers]
@@ -381,6 +422,10 @@ def __call__(
381422
inputs, cache=cache, input_embeddings=input_embeddings
382423
)
383424

425+
@property
426+
def model(self):
427+
return self.language_model.model
428+
384429
def sanitize(self, weights):
385430
sanitized = {}
386431
for key, value in weights.items():
@@ -517,7 +562,7 @@ def _repeat(p):
517562

518563
@property
519564
def layers(self):
520-
return self.language_model.model.layers
565+
return self.language_model.model.pipeline_layers
521566

522567
def make_cache(self):
523568
return self.language_model.make_cache()

0 commit comments

Comments
 (0)