Skip to content

Commit 863e534

Browse files
Merge pull request #4562 from AI-Hypercomputer:xibin/maxengine_oom1
PiperOrigin-RevId: 952223029
2 parents 94342eb + c3d233b commit 863e534

1 file changed

Lines changed: 23 additions & 31 deletions

File tree

src/maxtext/inference/maxengine/maxengine.py

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def _nnx_init_cache_dict(self, mode: str = MODEL_MODE_PREFILL) -> dict:
192192
"""Zero-filled pure-dict cache matching the abstract NNX model."""
193193
src = self._abstract_model_for_mode(mode)
194194
_, cache_state, _ = nnx.split(src, nnx.Cache, ...)
195-
cache_dict = cache_state.to_pure_dict() # pyrefly: ignore[missing-attribute]
195+
cache_dict = nnx.to_pure_dict(cache_state)
196196
return jax.tree.map(lambda x: jnp.zeros(x.shape, x.dtype), cache_dict)
197197

198198
def _nnx_run_model(
@@ -239,7 +239,7 @@ def _nnx_run_model(
239239
true_length=true_length,
240240
slot=slot,
241241
)
242-
new_cache = nnx.state(model, nnx.Cache).to_pure_dict()
242+
new_cache = nnx.to_pure_dict(nnx.state(model, nnx.Cache))
243243
return logits, new_cache
244244

245245
def generate_aot(
@@ -398,22 +398,13 @@ def _load_params_nnx(self, params, rng):
398398
forward. Same output as serve mode (absmax calibration), slower.
399399
"""
400400

401-
if params:
402-
print("Resharding given NNX params")
403-
_, params_abs, _ = nnx.split(self.model, nnx.Param, ...)
404-
# self.model is abstract (built via nnx.eval_shape), so its leaves carry logical
405-
# axis metadata but no physical .sharding. Resolve logical to physical here so
406-
# device_put actually reshards instead of being a no-op.
407-
with nn_partitioning.axis_rules(self.config.logical_axis_rules):
408-
target_shardings = sharding.nnx_construct_named_sharding(params_abs, self._mesh)
409-
params_state = jax.device_put(params, target_shardings)
410-
# We only need a concrete `rest` (RNG vars) for nnx.merge. create_nnx_sharded_model
411-
# builds the model with a jitted out_shardings so params are produced already
412-
# sharded, avoiding a single-device allocation of the full model (an OOM risk for
413-
# large models). self.model is abstract with no .sharding, so pass an explicit one.
401+
# Safely create the concrete PREFILL model once, avoiding OOM risks:
402+
# create_nnx_sharded_model builds the model with a jitted out_shardings so params
403+
# are produced already sharded, avoiding a single-device allocation of the full
404+
# model.
405+
with nn_partitioning.axis_rules(self.config.logical_axis_rules):
414406
_, full_abs = nnx.split(self.model)
415-
with nn_partitioning.axis_rules(self.config.logical_axis_rules):
416-
full_sharding = sharding.nnx_construct_named_sharding(full_abs, self._mesh)
407+
full_sharding = sharding.nnx_construct_named_sharding(full_abs, self._mesh)
417408
concrete_model = maxtext_utils_nnx.create_nnx_sharded_model(
418409
self.model,
419410
self._create_model_fn,
@@ -422,8 +413,18 @@ def _load_params_nnx(self, params, rng):
422413
)
423414
graphdef, _, _, rest_state = nnx.split(concrete_model, nnx.Param, nnx.Cache, ...)
424415
self.graphdef = graphdef
425-
self._nnx_rest_state = rest_state
426416
del concrete_model
417+
418+
if params:
419+
print("Resharding given NNX params")
420+
_, params_abs, _ = nnx.split(self.model, nnx.Param, ...)
421+
# self.model is abstract (built via nnx.eval_shape), so its leaves carry logical
422+
# axis metadata but no physical .sharding. Resolve logical to physical here so
423+
# device_put actually reshards instead of being a no-op.
424+
with nn_partitioning.axis_rules(self.config.logical_axis_rules):
425+
target_shardings = sharding.nnx_construct_named_sharding(params_abs, self._mesh)
426+
params_state = jax.device_put(params, target_shardings)
427+
self._nnx_rest_state = rest_state
427428
else:
428429
max_logging.log("Loading NNX params via from_pretrained")
429430
with self._mesh:
@@ -438,22 +439,14 @@ def _load_params_nnx(self, params, rng):
438439
# `_nnx_rest_state`. Param-only filtering would silently drop them and
439440
# the model would run with random qrhs values.
440441
_, params_state, _, loaded_rest_state = nnx.split(nnx_model, nnx.Param, nnx.Cache, ...)
441-
# `_prefill_jit` re-merges with `self.graphdef`, which must be the PREFILL
442-
# graphdef built in `__init__` (matching `_create_model_fn`). Don't
443-
# overwrite with the AR-mode graphdef from `from_pretrained` — the
444-
# PREFILL/AR attention ops have different cache variable shapes, and a
445-
# mismatch trips the `assert prefill_kv_cache` check inside attention_op.
446-
with nn_partitioning.axis_rules(self.config.logical_axis_rules):
447-
concrete_model = self._create_model_fn() # pyrefly: ignore[not-callable]
448-
graphdef, _, _, rest_state = nnx.split(concrete_model, nnx.Param, nnx.Cache, ...)
449442
# Overlay loaded non-Param/non-Cache leaves (e.g. AQT qrhs.frozen) onto
450443
# the PREFILL-mode rest_state. The PREFILL concrete_model already has
451444
# placeholder qrhs vars at the right paths; we just swap in the loaded
452445
# values. Anything only in `loaded_rest_state` (e.g. AR-only RNG slots)
453446
# is ignored. We keep PREFILL rest_state as the base so RNG variables
454447
# match the PREFILL graphdef's expectations.
455-
loaded_rest_dict = loaded_rest_state.to_pure_dict() # pyrefly: ignore[missing-attribute]
456-
rest_dict = rest_state.to_pure_dict() # pyrefly: ignore[missing-attribute]
448+
loaded_rest_dict = nnx.to_pure_dict(loaded_rest_state)
449+
rest_dict = nnx.to_pure_dict(rest_state)
457450

458451
def _overlay(dst, src):
459452
if isinstance(dst, dict) and isinstance(src, dict):
@@ -469,9 +462,8 @@ def _overlay(dst, src):
469462

470463
rest_dict = _overlay(rest_dict, loaded_rest_dict)
471464
nnx.replace_by_pure_dict(rest_state, rest_dict)
472-
self.graphdef = graphdef
473465
self._nnx_rest_state = rest_state
474-
del nnx_model, concrete_model
466+
del nnx_model, loaded_rest_state, loaded_rest_dict, rest_dict
475467

476468
self.abstract_params = jax.tree.map(
477469
lambda x: jax.ShapeDtypeStruct(shape=x.shape, dtype=x.dtype, sharding=x.sharding)
@@ -2011,7 +2003,7 @@ def _logical_axes_for(var):
20112003
cache_state,
20122004
is_leaf=lambda v: isinstance(v, nnx.Variable),
20132005
)
2014-
self.kv_cache_annotations_named = annotations_state.to_pure_dict()
2006+
self.kv_cache_annotations_named = nnx.to_pure_dict(annotations_state)
20152007

20162008
return {
20172009
"logits": jnp.zeros((batch, 1, vocab), dtype=jnp.float32),

0 commit comments

Comments
 (0)