Skip to content

Commit ab20f71

Browse files
lkolluru05camiloCienet
authored andcommitted
working replica-resize
1 parent 602b509 commit ab20f71

9 files changed

Lines changed: 85 additions & 21 deletions

File tree

axlearn/common/elastic_input.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
from axlearn.common.config import REQUIRED, Required, config_class, maybe_set_config
5050
from axlearn.common.input_dispatch import BaseInputDispatcher, _validate_logical_feed_shapes
5151
from axlearn.common.module import Module
52-
from axlearn.common.utils import Nested, Tensor
52+
from axlearn.common.utils import Nested, Tensor, live_devices
5353

5454

5555
class ElasticSpmdInputDispatcher(BaseInputDispatcher):
@@ -72,7 +72,10 @@ class Config(BaseInputDispatcher.Config):
7272

7373
@property
7474
def is_in_elastic_mode(self) -> bool:
75+
print("In is_in_elastic_mode by lkolluru")
7576
cfg = self.config
77+
print("cfg.num_max_slices by lkolluru: ", cfg.num_max_slices)
78+
print("slice_count by lkolluru: ", slice_count())
7679
if cfg.num_max_slices is None:
7780
return False
7881
else:
@@ -165,11 +168,23 @@ def fid2pids(feed_id):
165168
else:
166169
self._feed_logical_batch_size = cfg.global_logical_batch_size // self.feed_count
167170

171+
adjusted_device_physical_batch_size = math.ceil(
172+
self._device_physical_batch_size * (cfg.num_max_slices / slice_count())
173+
)
174+
print(
175+
"adjusted_device_physical_batch_size outside elastic by lkolluru: ",
176+
adjusted_device_physical_batch_size,
177+
)
178+
168179
if self.is_in_elastic_mode:
169180
print(" In elastic mode lkolluru")
170181
adjusted_device_physical_batch_size = math.ceil(
171182
self._device_physical_batch_size * (cfg.num_max_slices / slice_count())
172183
)
184+
print(
185+
"adjusted_device_physical_batch_size inside elastic by lkolluru: ",
186+
adjusted_device_physical_batch_size,
187+
)
173188
padding_per_device = (
174189
adjusted_device_physical_batch_size - self._device_physical_batch_size
175190
)
@@ -408,9 +423,14 @@ def _padded_select(path, x, y):
408423

409424
def slice_count() -> int:
410425
"""Returns the number of slices."""
411-
slice_cnt_val = len(set(d.slice_index for d in jax.devices() if hasattr(d, "slice_index"))) or 1
426+
# slice_cnt_val=len(set(d.slice_index for d in jax.devices() if hasattr(d, "slice_index"))) or 1
427+
# print("slice_count by lkolluru: ", slice_cnt_val)
428+
# return len(set(d.slice_index for d in jax.devices() if hasattr(d, "slice_index"))) or 1
429+
slice_cnt_val = (
430+
len(set(d.slice_index for d in live_devices() if hasattr(d, "slice_index"))) or 1
431+
)
412432
print("slice_count by lkolluru: ", slice_cnt_val)
413-
return len(set(d.slice_index for d in jax.devices() if hasattr(d, "slice_index"))) or 1
433+
return len(set(d.slice_index for d in live_devices() if hasattr(d, "slice_index"))) or 1
414434

415435

416436
def process_count_per_slice() -> int:
@@ -419,7 +439,8 @@ def process_count_per_slice() -> int:
419439
len(
420440
set(
421441
d.process_index
422-
for d in jax.devices()
442+
# for d in jax.devices()
443+
for d in live_devices()
423444
if hasattr(d, "slice_index") and d.slice_index == 0
424445
)
425446
)

axlearn/common/flash_attention/neuron_attention.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
from axlearn.common.attention_bias import BaseAttentionBias, CausalAttentionBias, split
1818
from axlearn.common.flash_attention.common import BaseFlashAttention, repeat_kv_heads
1919
from axlearn.common.kv_cache.base_kv_cache import BaseKVCache
20-
from axlearn.common.utils import Nested
20+
from axlearn.common.utils import Nested, live_devices
2121

2222
# pytype: enable=import-error
2323

2424
Tensor = jax.Array
25-
lnc = 2 if jax.devices()[0].device_kind == "NC_v3d" else 1
25+
# lnc = 2 if jax.devices()[0].device_kind == "NC_v3d" else 1
26+
lnc = 2 if live_devices()[0].device_kind == "NC_v3d" else 1
2627

2728

2829
# TODO(apoorvtintin): Add segment IDs as an argument when the kernel supports it.

axlearn/common/launch.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from absl import flags, logging
4646

4747
from axlearn.common.status_server import StatusHTTPServer
48-
from axlearn.common.utils import get_data_dir
48+
from axlearn.common.utils import get_data_dir, live_devices
4949
from axlearn.common.utils_spmd import setup as setup_spmd
5050

5151
# pylint: enable=wrong-import-position
@@ -142,7 +142,8 @@ def setup():
142142
status_server = StatusHTTPServer(FLAGS.status_port)
143143
status_server.start()
144144

145-
devices = jax.devices()
145+
# devices = jax.devices() ##### may be update here ####
146+
devices = live_devices()
146147
logging.info("Devices: %s", devices)
147148
local_devices = jax.local_devices()
148149
logging.info("Local Devices: %s", local_devices)

axlearn/common/launch_trainer.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from axlearn.common import measurement
1414
from axlearn.common.config import TrainerConfigFn, get_named_trainer_config
1515
from axlearn.common.trainer import SpmdTrainer, select_mesh_config
16-
from axlearn.common.utils import MeshShape, get_data_dir, infer_mesh_shape
16+
from axlearn.common.utils import MeshShape, get_data_dir, infer_mesh_shape, live_devices
1717

1818
# Trainer-specific flags.
1919
flags.DEFINE_string(
@@ -148,7 +148,8 @@ def get_trainer_config(
148148
if flag_values.mesh_selector is not None:
149149
select_mesh_config(trainer_config, mesh_selector=flag_values.mesh_selector)
150150
trainer_config.mesh_axis_names = trainer_config.mesh_axis_names or ("data", "model")
151-
trainer_config.mesh_shape = trainer_config.mesh_shape or (len(jax.devices()), 1)
151+
# trainer_config.mesh_shape = trainer_config.mesh_shape or (len(jax.devices()), 1)
152+
trainer_config.mesh_shape = trainer_config.mesh_shape or (len(live_devices()), 1)
152153
if isinstance(trainer_config.mesh_shape, MeshShape):
153154
trainer_config.mesh_shape = infer_mesh_shape(trainer_config.mesh_shape)
154155
trainer_config.start_trace_steps = [int(el) for el in flag_values.trace_at_steps]

axlearn/common/launch_trainer_main.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,24 @@
1717
def main(_):
1818
measurement.initialize(flags.FLAGS)
1919
launch.setup()
20-
trainer_config = launch_trainer.get_trainer_config()
21-
trainer_config.set(recorder=config_for_function(lambda: measurement.global_recorder))
22-
measurement.start_monitoring()
20+
# trainer_config = launch_trainer.get_trainer_config()
21+
# trainer_config.set(recorder=config_for_function(lambda: measurement.global_recorder))
22+
# measurement.start_monitoring()
2323

2424
if pathwaysutils.is_pathways_backend_used() and enable_elastic_training:
2525

2626
def train():
27+
# measurement.initialize(flags.FLAGS)
28+
# launch.setup()
29+
trainer_config = launch_trainer.get_trainer_config()
30+
trainer_config.set(recorder=config_for_function(lambda: measurement.global_recorder))
31+
measurement.start_monitoring()
2732
launch_trainer.run_trainer(trainer_config)
2833

2934
utils.elastic_manager = manager.Manager()
3035

36+
# utils.elastic_manager.live_devices = live_devices()
37+
3138
if enable_pause_resume:
3239
print("Pathways backend with pause resume being used")
3340
train = utils.elastic_manager.pause_resume(
@@ -66,6 +73,11 @@ def pre_callback():
6673

6774
train()
6875
else:
76+
measurement.initialize(flags.FLAGS)
77+
launch.setup()
78+
trainer_config = launch_trainer.get_trainer_config()
79+
trainer_config.set(recorder=config_for_function(lambda: measurement.global_recorder))
80+
measurement.start_monitoring()
6981
launch_trainer.run_trainer(trainer_config)
7082

7183

axlearn/common/state_builder.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
flatten_items,
4848
get_data_dir,
4949
infer_mesh_shape,
50+
live_devices,
5051
set_data_dir,
5152
)
5253

@@ -479,7 +480,10 @@ def target_to_source(self, target: Builder.State) -> tuple[Builder.State, Any]:
479480
cfg.mesh_axis_names or trainer_cfg.mesh_axis_names or ("data", "model")
480481
)
481482
trainer_cfg.mesh_shape = infer_mesh_shape(
482-
cfg.mesh_shape or trainer_cfg.mesh_shape or (len(jax.devices()), 1)
483+
# cfg.mesh_shape or trainer_cfg.mesh_shape or (len(jax.devices()), 1)
484+
cfg.mesh_shape
485+
or trainer_cfg.mesh_shape
486+
or (len(live_devices()), 1)
483487
)
484488
# Reset datasets and evalers for the pretrained model config.
485489
# This input is not used. Set global_batch_size to 0 by default.

axlearn/common/trainer.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from jax import numpy as jnp
1919
from jax.experimental import multihost_utils
2020
from jax.experimental.pjit import pjit
21+
from pathwaysutils.elastic import manager
2122

2223
from axlearn.common import file_system as fs
2324
from axlearn.common import measurement, utils
@@ -59,6 +60,7 @@
5960
count_model_params,
6061
flatten_items,
6162
host_to_global_specs,
63+
live_devices,
6264
match_regex_rules,
6365
thread_stack_traces,
6466
)
@@ -407,7 +409,8 @@ def model_params_for_eval(self):
407409

408410
def _step_log(self, msg, *args, **kwargs):
409411
logging.info(
410-
"%s process % 3d step % 8d] " + msg,
412+
"live_devices: %d ; %s process % 3d step % 8d ] " + msg,
413+
len(live_devices()),
411414
self.path(),
412415
jax.process_index(),
413416
-1 if self.step is None else self.step,
@@ -646,7 +649,7 @@ def run(
646649
)
647650
self.vlog(3, "Done step %s", self.step)
648651
num_steps += 1
649-
if num_steps % 100 == 0:
652+
if num_steps % 5 == 0:
650653
now = time.perf_counter()
651654
average_step_time = (now - start_time) / num_steps
652655
self._step_log("Average step time: %s seconds", average_step_time)
@@ -1018,6 +1021,10 @@ def save_checkpoint(self, evaler_summaries: Optional[dict[str, Any]]) -> Optiona
10181021
self.checkpointer.save(
10191022
step=self.step, state=ckpt_state, evaler_summaries=evaler_summaries
10201023
)
1024+
print("Checkpoint saved !!!!")
1025+
if utils.elastic_manager is not None and utils.elastic_manager.new_slice_event.is_set():
1026+
logging.info("Interrupting as a new elastic slice event is seen")
1027+
raise manager.ScaleUpSignalError()
10211028

10221029
def _restore_from_builder(self) -> Optional[TrainerStateBuilder.State]:
10231030
"""Restores trainer state by building it with init_state_builder."""
@@ -1069,7 +1076,8 @@ def _get_compiled_train_step_fn(
10691076
cfg: SpmdTrainer.Config = self.config
10701077
# Get device kinds and assert that they are homogenous.
10711078
# TODO(markblee): Get devices from self._mesh.devices.
1072-
device_kinds = set(d.device_kind for d in jax.devices())
1079+
# device_kinds = set(d.device_kind for d in jax.devices()) #### may be here #####
1080+
device_kinds = set(d.device_kind for d in live_devices())
10731081
if len(device_kinds) != 1:
10741082
raise RuntimeError(f"Heterogenous device kinds ({device_kinds}) are not supported.")
10751083
device_kind = device_kinds.pop()

axlearn/common/utils.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import attr
4444
import jax
4545
import numpy as np
46+
import pathwaysutils
4647
from absl import logging
4748
from jax import numpy as jnp
4849
from jax._src.ad_checkpoint import name_p
@@ -93,6 +94,19 @@
9394
elastic_manager: manager.Manager | None = None
9495

9596

97+
def live_devices():
98+
device_list = jax.devices()
99+
100+
if pathwaysutils.is_pathways_backend_used() and elastic_manager is not None:
101+
return [d for d in device_list if d.slice_index in elastic_manager.active_slice_indices]
102+
else:
103+
return device_list
104+
105+
106+
def live_slice_indices() -> set[int]:
107+
return {d.slice_index for d in live_devices()}
108+
109+
96110
@dataclasses.dataclass
97111
class HybridMeshShape:
98112
"""A mesh shape for hybrid (i.e., ICI and DCN) parallelism.
@@ -1799,7 +1813,8 @@ def create_device_mesh(
17991813
NotImplementedError: If not all devices have the same platform.
18001814
"""
18011815
if devices is None:
1802-
devices = jax.devices()
1816+
# devices = jax.devices() ##### may be update here ###
1817+
devices = live_devices()
18031818
devices = np.asarray(devices)
18041819

18051820
# Check if the devices are part of a multi-granule configuration.
@@ -1910,7 +1925,8 @@ def infer_mesh_shape(mesh_shape: MeshShape, *, num_devices: Optional[int] = None
19101925
# Handle the case with one -1.
19111926
prod = math.prod(mesh_shape, start=-1)
19121927
if num_devices is None:
1913-
num_devices = len(jax.devices())
1928+
# num_devices = len(jax.devices()) ###### may be update here ####
1929+
num_devices = len(live_devices())
19141930
if num_devices % prod != 0:
19151931
raise ValueError(
19161932
f"Unable to infer -1 in mesh shape {mesh_shape} as num_devices {num_devices} "

axlearn/experiments/text/gpt/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ def config_fn() -> InstantiableConfig:
765765

766766
train_dispatcher_cfg = elastic_input.ElasticSpmdInputDispatcher.default_config().set(
767767
global_logical_batch_size=train_batch_size,
768-
num_max_slices=1,
768+
num_max_slices=2,
769769
)
770770

771771
cfg.input = elastic_input.ElasticInput.default_config().set(
@@ -809,7 +809,7 @@ def config_fn() -> InstantiableConfig:
809809
cfg.evalers[name] = evaler_cfg
810810
# Summaries and checkpoints.
811811
cfg.checkpointer.save_policy = config_for_function(every_n_steps_and_last_policy).set(
812-
n=save_every_n_steps or min(eval_every_n_steps, 5_000),
812+
n=save_every_n_steps or min(eval_every_n_steps, 50),
813813
max_step=max_step,
814814
)
815815
cfg.checkpointer.keep_every_n_steps = min(max_step, keep_every_n_steps)

0 commit comments

Comments
 (0)