Skip to content

Commit 365119d

Browse files
committed
add a new record_slice_state helper that queries live slice counts and logs them to the GoodputRecorder
1 parent da8b70a commit 365119d

2 files changed

Lines changed: 59 additions & 0 deletions

File tree

src/maxtext/utils/elastic_utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,30 @@
2828
pending_elastic_event_type = None
2929

3030

31+
def record_slice_state(recorder, active_slices_override: int | None = None) -> None:
32+
"""Queries live slice counts and logs them to the GoodputRecorder."""
33+
if recorder is None or not pathwaysutils.is_pathways_backend_used() or elastic_manager is None:
34+
return
35+
36+
available_slices = len(pathwaysutils.elastic.get_active_slice_indices())
37+
active_slices = active_slices_override if active_slices_override is not None else len(elastic_manager.active_slice_indices)
38+
total_slices = len(elastic_manager.slice_to_devices)
39+
40+
if recorder:
41+
recorder.record_slice_state(
42+
available_slices=available_slices,
43+
active_slices=active_slices,
44+
total_slices=total_slices
45+
)
46+
3147
def record_elastic_event_start(recorder, config) -> None:
3248
"""Records start of an elastic scale up event."""
3349
global pending_elastic_event_type
3450
event_type = 'elastic_scale_up' if is_scale_up_event(config) else 'elastic_slice_down'
3551
pending_elastic_event_type = event_type
3652
if recorder:
3753
recorder.record_custom_badput_event_start_time(custom_badput_event_type=event_type)
54+
record_slice_state(recorder, active_slices_override=0)
3855

3956

4057
def record_elastic_wait_end_and_reinit_start(recorder) -> None:
@@ -47,6 +64,7 @@ def record_elastic_wait_end_and_reinit_start(recorder) -> None:
4764
if recorder:
4865
recorder.record_custom_badput_event_end_time(custom_badput_event_type=event_type)
4966
recorder.record_custom_badput_event_start_time(custom_badput_event_type='elastic_reinitialization')
67+
record_slice_state(recorder)
5068
pending_reinit_recorder = recorder
5169

5270

@@ -57,6 +75,7 @@ def record_elastic_reinit_end() -> None:
5775
pending_reinit_recorder.record_custom_badput_event_end_time(
5876
custom_badput_event_type='elastic_reinitialization'
5977
)
78+
record_slice_state(pending_reinit_recorder)
6079
pending_reinit_recorder = None
6180

6281

tests/unit/elastic_utils_test.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ def setUp(self):
7070

7171
# Configure default behaviors if needed
7272
self.fake_pathwaysutils.is_pathways_backend_used.return_value = True
73+
self.fake_pathwaysutils.elastic = Mock()
74+
self.fake_pathwaysutils.elastic.get_active_slice_indices.return_value = [0, 1]
7375
self.fake_jax.process_index.return_value = 0
7476

7577
# Inject fakes into elastic_utils namespace
@@ -97,9 +99,37 @@ def tearDown(self):
9799
elastic_utils.pending_elastic_event_type = None
98100
super().tearDown()
99101

102+
103+
def test_record_slice_state(self):
104+
elastic_utils.elastic_manager = self.fake_manager
105+
self.fake_manager.active_slice_indices = {0}
106+
self.fake_manager.slice_to_devices = {0: [FakeDevice()], 1: [FakeDevice()]}
107+
self.fake_pathwaysutils.elastic.get_active_slice_indices.return_value = {0, 1}
108+
109+
fake_recorder = Mock()
110+
fake_recorder.record_slice_state = Mock()
111+
112+
elastic_utils.record_slice_state(fake_recorder)
113+
114+
fake_recorder.record_slice_state.assert_called_once_with(
115+
available_slices=2,
116+
active_slices=1,
117+
total_slices=2
118+
)
119+
120+
fake_recorder.record_slice_state.reset_mock()
121+
elastic_utils.record_slice_state(fake_recorder, active_slices_override=0)
122+
fake_recorder.record_slice_state.assert_called_once_with(
123+
available_slices=2,
124+
active_slices=0,
125+
total_slices=2
126+
)
127+
100128
def test_elastic_enabled(self):
101129
config = FakeConfig()
102130
self.fake_pathwaysutils.is_pathways_backend_used.return_value = True
131+
self.fake_pathwaysutils.elastic = Mock()
132+
self.fake_pathwaysutils.elastic.get_active_slice_indices.return_value = [0, 1]
103133
config.elastic_enabled = True
104134
self.assertTrue(elastic_utils.elastic_enabled(config))
105135

@@ -144,6 +174,8 @@ def test_live_devices_no_pathways(self):
144174
def test_live_devices_pathways(self):
145175
"""Tests live_devices when pathways is used."""
146176
self.fake_pathwaysutils.is_pathways_backend_used.return_value = True
177+
self.fake_pathwaysutils.elastic = Mock()
178+
self.fake_pathwaysutils.elastic.get_active_slice_indices.return_value = [0, 1]
147179
device0 = FakeDevice(slice_index=0)
148180
device1 = FakeDevice(slice_index=1)
149181
self.fake_jax.devices.return_value = [device0, device1]
@@ -156,6 +188,8 @@ def test_live_devices_pathways(self):
156188
def test_elastic_retry_disabled(self):
157189
"""Tests elastic_retry when disabled but pathways is used."""
158190
self.fake_pathwaysutils.is_pathways_backend_used.return_value = True
191+
self.fake_pathwaysutils.elastic = Mock()
192+
self.fake_pathwaysutils.elastic.get_active_slice_indices.return_value = [0, 1]
159193
config = FakeConfig()
160194
config.elastic_enabled = False
161195
msg = (
@@ -315,6 +349,7 @@ def test_record_elastic_event_start(self):
315349
fake_recorder.record_custom_badput_event_start_time.assert_called_once_with(
316350
custom_badput_event_type='elastic_slice_down'
317351
)
352+
fake_recorder.record_slice_state.assert_called_once()
318353
self.assertEqual(elastic_utils.pending_elastic_event_type, 'elastic_slice_down')
319354

320355
def test_record_elastic_event_start_scale_up(self):
@@ -329,6 +364,7 @@ def test_record_elastic_event_start_scale_up(self):
329364
fake_recorder.record_custom_badput_event_start_time.assert_called_once_with(
330365
custom_badput_event_type='elastic_scale_up'
331366
)
367+
fake_recorder.record_slice_state.assert_called_once()
332368

333369
def test_record_elastic_wait_end_and_reinit_start_noop_on_first_attempt(self):
334370
"""Tests recording elastic event end and elastic reinit start."""
@@ -354,6 +390,7 @@ def test_record_elastic_wait_end_and_reinit_start(self):
354390
fake_recorder.record_custom_badput_event_start_time.assert_called_once_with(
355391
custom_badput_event_type='elastic_reinitialization'
356392
)
393+
fake_recorder.record_slice_state.assert_called_once()
357394
self.assertIs(elastic_utils.pending_reinit_recorder, fake_recorder)
358395
self.assertIsNone(elastic_utils.pending_elastic_event_type)
359396

@@ -367,6 +404,7 @@ def test_record_elastic_reinit_end(self):
367404
fake_recorder.record_custom_badput_event_end_time.assert_called_once_with(
368405
custom_badput_event_type='elastic_reinitialization'
369406
)
407+
fake_recorder.record_slice_state.assert_called_once()
370408
self.assertIsNone(elastic_utils.pending_reinit_recorder)
371409

372410
def test_record_elastic_reinit_end_on_cold_start(self):
@@ -389,6 +427,8 @@ def __setattr__(self, name, value):
389427

390428
config = ReadOnlyConfig()
391429
self.fake_pathwaysutils.is_pathways_backend_used.return_value = True
430+
self.fake_pathwaysutils.elastic = Mock()
431+
self.fake_pathwaysutils.elastic.get_active_slice_indices.return_value = [0, 1]
392432

393433
# Should not raise ValueError
394434
elastic_utils.ensure_elastic_manager_initialized(config)

0 commit comments

Comments
 (0)