Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7189e02
Replica resize related changes
lukebaumann May 8, 2026
350be3d
Adding debugging watchdogs
lukebaumann May 8, 2026
6005699
Optimize elastic resizing workflow by running setup in child thread
lukebaumann May 8, 2026
e46959d
Update scale-up check to use available_inactive_slices set
lukebaumann Jun 23, 2026
f5dbe2c
Add pathways elastic training resiliency (asynchronous host snapshott…
lukebaumann May 14, 2026
463e6dc
Add scale test debugging and logging
lukebaumann May 14, 2026
4f0d0b3
Fix elasticity bugs: wait for slices at startup and recalculate batch…
lukebaumann May 15, 2026
78304e2
Fix save_pytree mismatch: save state_dict instead of TrainState after…
lukebaumann May 15, 2026
1986e9e
Fix elasticity recovery: wait for active slices and add empty snapsho…
lukebaumann May 15, 2026
33a9817
Remove comment and avoid unpacking gbs_load_start in recover
lukebaumann Jun 5, 2026
5749742
Fix recover to use metric_logger_instance
lukebaumann Jun 10, 2026
e834104
Use Snapshotter for metrics recovery and handle non-Array leaves
lukebaumann Jul 1, 2026
e14ab31
Support NNX and Linen in elasticity snapshot functions
lukebaumann Jul 2, 2026
d8783d4
Support TrainStateNNX in Snapshotter save/load/recover for elastic tr…
lukebaumann Jul 2, 2026
218564b
Import train_state_nnx in train.py
lukebaumann Jul 2, 2026
88db723
Support nnx.State in Snapshotter save/load/recover for NNX models
lukebaumann Jul 2, 2026
7e8dd8f
Restore p_train_step execution line in training_loop_iteration
lukebaumann Jul 2, 2026
0823ced
Enable elastic recovery for pure_nnx in recover()
lukebaumann Jul 2, 2026
28ec2f9
Set min_slices=1 for elastic slice-down recovery in recover()
lukebaumann Jul 2, 2026
367104a
Unpack recorder in recover()
lukebaumann Jul 2, 2026
abcee2d
Fix TrainStateNNX state extraction and update in recover()
lukebaumann Jul 2, 2026
738d44d
Convert pinned_host memory kind to device before split_by_mesh_axis i…
lukebaumann Jul 2, 2026
09ea1b6
Add addressable_shards fallback in snapshot.py for host-pinned arrays
lukebaumann Jul 2, 2026
a0958f4
Simplify NNX state extraction and restore in recover()
lukebaumann Jul 2, 2026
4c484d0
Fix all snapshot saving places to use uniform model and optimizer PyT…
lukebaumann Jul 2, 2026
ce7bd0d
Split TrainStateNNX to nnx.State in recover() to match p_train_step i…
lukebaumann Jul 2, 2026
a00e2fd
Use safe leaf-by-leaf block_until_ready in load_pytree in snapshot.py
lukebaumann Jul 2, 2026
c3bc9af
Pass GraphDef jit_model to jit_train_and_eval_step in recover() for NNX
lukebaumann Jul 2, 2026
97713ad
Safely convert step array to int in recover() to prevent DATA_LOSS ex…
lukebaumann Jul 2, 2026
0c6ee16
Fix Data Loss issue in DataLoader.
abhinavclemson Jul 11, 2026
551a688
Fix maxtext import issue.
abhinavclemson Jul 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/maxtext/common/checkpointing.py
Original file line number Diff line number Diff line change
Expand Up @@ -943,9 +943,11 @@ def map_to_pspec(data):
)
_assert_no_shaped_dtype_struct(restored_state)
return {"items": restored_state}, None
if checkpoint_manager is None:
max_logging.log("Checkpoint manager is None, skipping checkpoint restoration.")
else:
max_logging.log("No existing checkpoints found, not restoring checkpoint.")
return None, None
return None, None


def setup_checkpoint_logger(config) -> Any | None: # pytype: disable=attribute-error
Expand Down
6 changes: 6 additions & 0 deletions src/maxtext/common/data_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from maxtext.trainers.diloco import diloco
from maxtext.utils import exceptions
from maxtext.utils.sharding import get_input_data_sharding
from maxtext.utils import elastic_utils


class DataLoader:
Expand Down Expand Up @@ -63,6 +64,11 @@ def load_next_batch_pre_sharding(self):
self.last_batch = example_batch
self.check_example_batch()
except Exception as e: # pylint: disable=broad-except
if elastic_utils.elastic_enabled(self.config) and (
isinstance(e, jax.errors.JaxRuntimeError)
or isinstance(e, elastic_utils.manager.ScaleUpSignalError)
):
raise
if isinstance(e, StopIteration):
raise exceptions.StopTraining(f"You may have run out of training data. Received {type(e)} exception: ({e})")
else:
Expand Down
41 changes: 39 additions & 2 deletions src/maxtext/common/metric_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ def record_activation_metrics(output_metrics, intermediate_outputs, config):
output_metrics["scalar"][f"{label}/layer_{layer_num:03d}"] = per_layer[layer_num]


def _pin_metrics_to_host(metrics):
"""Pins metrics arrays to host memory."""
def _to_pinned_host(x):
if hasattr(x, "sharding"):
return jax.device_put(x, x.sharding.with_memory_kind("pinned_host"))
return x
return jax.tree.map(_to_pinned_host, metrics)


class MetadataKey(enum.Enum):
PER_DEVICE_TFLOPS = "per_device_tflops"
PER_DEVICE_TOKENS = "per_device_tokens"
Expand Down Expand Up @@ -149,7 +158,15 @@ def _log_training_metrics(self, metrics, step):
"""Handles training-specific metric logging."""
# Skip logging if in profiler activation/deactivation steps
# TODO(b/456828037): Switch to subprocess profiling to avoid timing artifacts at boundary steps.
scalars = metrics["scalar"]
def _safe_get(val):
if isinstance(val, jax.Array):
try:
return jax.device_get(val)
except Exception:
return 0
return val

scalars = jax.tree.map(_safe_get, metrics["scalar"])
loss = scalars["learning/loss"]
is_rampup = step < self.config.rampup_end_step
is_metric_hidden_step = self.config.hide_profiler_step_metric and self._is_profiler_boundary_step(step)
Expand All @@ -164,10 +181,12 @@ def _log_training_metrics(self, metrics, step):
f"completed profiler activation/deactivation step: {step}",
)
else:
active_slices = len(elastic_utils.live_slice_indices(self.config)) if elastic_utils.elastic_enabled(self.config) else 1
log_parts.extend(
[
f"completed step: {step}",
f"seconds: {scalars['perf/step_time_seconds']:.3f}",
f"active_slices: {active_slices}",
]
)
if elastic_utils.elastic_enabled(self.config):
Expand Down Expand Up @@ -384,13 +403,31 @@ def buffer_and_write_metrics(self, metrics, step, step_time_delta=None, is_train
self._flush_one_buffered_entry(self.buffered_metrics.pop(0))
if is_training:
self.record_train_metrics(metrics, step, step_time_delta.total_seconds())
self.buffered_metrics.append(("train", step, metrics, step_time_delta))
# Pinned host memory transfer to allow recovery from host safely without default device bindings
metrics_pinned = _pin_metrics_to_host(metrics)
self.buffered_metrics.append(("train", step, metrics_pinned, step_time_delta))
if self._pending_eval_step_count > 0:
self._finalize_eval_metrics(step)
else:
self._pending_eval_step_count += 1
self.buffered_metrics.append(("eval", step, metrics, step_time_delta))

def recover_metrics(self, recovered_metrics=None):
"""Flushes and prints buffered metrics safely during recovery, then clears the buffer."""
if recovered_metrics is not None:
try:
scalars = recovered_metrics.get("scalar", {})
loss = float(scalars.get("learning/loss", 0.0))
step_time = float(scalars.get("perf/step_time_seconds", 0.0))
max_logging.log(
f"[METRIC RECOVERY] Successfully recovered metrics via Snapshotter | Loss: {loss:.3f} | Step Time: {step_time:.3f}s"
)
except Exception as e:
max_logging.log(f"[METRIC RECOVERY] Failed to read Snapshotter recovered metrics: {e}")

# Cleanly clear the buffer to prevent dead device reference errors downstream
self.buffered_metrics.clear()

def _flush_one_buffered_entry(self, entry):
"""Dispatches a single buffered entry to the writer."""
kind = entry[0]
Expand Down
3 changes: 3 additions & 0 deletions src/maxtext/configs/base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,9 @@ distill_beta_schedule: "constant"
##### Elastic training parameters
# Elastic training is Pathways-specific and does not work on McJAX.
elastic_enabled: false
elastic_backup_kind: "snapshot"
elastic_snapshot_interval: 10
elastic_new_slice_check_period: 10
elastic_timeout_seconds: 300
elastic_max_retries: 10
elastic_min_slice_count: -1
3 changes: 3 additions & 0 deletions src/maxtext/configs/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1913,6 +1913,9 @@ class ElasticTraining(BaseModel):
"""

elastic_enabled: bool = Field(False, description="Whether to enable elastic training.")
elastic_backup_kind: str = Field("snapshot", description="The kind of backup to use for elastic training: 'checkpoint' or 'snapshot'.")
elastic_snapshot_interval: int = Field(10, description="The interval in steps to save snapshots to host memory.")
elastic_new_slice_check_period: int = Field(10, description="The interval in seconds to poll for newly joined active slices.")
elastic_timeout_seconds: int = Field(
300,
description=(
Expand Down
Loading
Loading