Skip to content

Commit d4a529c

Browse files
committed
Squashed commits for GoodputRecorder slice state logic
1 parent 518ad8d commit d4a529c

2 files changed

Lines changed: 72 additions & 0 deletions

File tree

src/maxtext/utils/elastic_utils.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,29 @@
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+
recorder.record_elastic_slice_counts(
41+
available_slices=available_slices,
42+
active_slices=active_slices,
43+
total_slices=total_slices
44+
)
45+
3146
def record_elastic_event_start(recorder, config) -> None:
3247
"""Records start of an elastic scale up event."""
3348
global pending_elastic_event_type
3449
event_type = "elastic_scale_up" if is_scale_up_event(config) else "elastic_slice_down"
3550
pending_elastic_event_type = event_type
3651
if recorder:
3752
recorder.record_custom_badput_event_start_time(custom_badput_event_type=event_type)
53+
record_slice_state(recorder, active_slices_override=0)
3854

3955

4056
def record_elastic_wait_end_and_reinit_start(recorder) -> None:
@@ -46,6 +62,8 @@ def record_elastic_wait_end_and_reinit_start(recorder) -> None:
4662
pending_elastic_event_type = None
4763
if recorder:
4864
recorder.record_custom_badput_event_end_time(custom_badput_event_type=event_type)
65+
recorder.record_custom_badput_event_start_time(custom_badput_event_type='elastic_reinitialization')
66+
record_slice_state(recorder)
4967
recorder.record_custom_badput_event_start_time(custom_badput_event_type="elastic_reinitialization")
5068
pending_reinit_recorder = recorder
5169

@@ -54,6 +72,10 @@ def record_elastic_reinit_end() -> None:
5472
"""Records end of elastic reinitialization event."""
5573
global pending_reinit_recorder
5674
if pending_reinit_recorder is not None:
75+
pending_reinit_recorder.record_custom_badput_event_end_time(
76+
custom_badput_event_type='elastic_reinitialization'
77+
)
78+
record_slice_state(pending_reinit_recorder)
5779
pending_reinit_recorder.record_custom_badput_event_end_time(custom_badput_event_type="elastic_reinitialization")
5880
pending_reinit_recorder = None
5981

tests/unit/elastic_utils_test.py

Lines changed: 50 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_elastic_slice_counts = Mock()
111+
112+
elastic_utils.record_slice_state(fake_recorder)
113+
114+
fake_recorder.record_elastic_slice_counts.assert_called_once_with(
115+
available_slices=2,
116+
active_slices=1,
117+
total_slices=2
118+
)
119+
120+
fake_recorder.record_elastic_slice_counts.reset_mock()
121+
elastic_utils.record_slice_state(fake_recorder, active_slices_override=0)
122+
fake_recorder.record_elastic_slice_counts.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]
@@ -168,6 +200,8 @@ def test_live_devices_disabled(self):
168200
def test_elastic_retry_disabled(self):
169201
"""Tests elastic_retry when disabled but pathways is used."""
170202
self.fake_pathwaysutils.is_pathways_backend_used.return_value = True
203+
self.fake_pathwaysutils.elastic = Mock()
204+
self.fake_pathwaysutils.elastic.get_active_slice_indices.return_value = [0, 1]
171205
config = FakeConfig()
172206
config.elastic_enabled = False
173207
msg = (
@@ -319,6 +353,7 @@ def test_record_elastic_event_start(self):
319353
"""Tests recording an elastic slice down start."""
320354
elastic_utils.elastic_manager = self.fake_manager
321355
self.fake_manager.new_slice_event.is_set.return_value = False
356+
self.fake_manager.slice_to_devices = {0: [FakeDevice()], 1: [FakeDevice()]}
322357
fake_recorder = Mock()
323358
config = FakeConfig()
324359

@@ -327,12 +362,15 @@ def test_record_elastic_event_start(self):
327362
fake_recorder.record_custom_badput_event_start_time.assert_called_once_with(
328363
custom_badput_event_type="elastic_slice_down"
329364
)
365+
fake_recorder.record_elastic_slice_counts.assert_called_once()
366+
self.assertEqual(elastic_utils.pending_elastic_event_type, 'elastic_slice_down')
330367
self.assertEqual(elastic_utils.pending_elastic_event_type, "elastic_slice_down")
331368

332369
def test_record_elastic_event_start_scale_up(self):
333370
"""Tests recording an elastic slice scale up start."""
334371
elastic_utils.elastic_manager = self.fake_manager
335372
self.fake_manager.new_slice_event.is_set.return_value = True
373+
self.fake_manager.slice_to_devices = {0: [FakeDevice()], 1: [FakeDevice()]}
336374
fake_recorder = Mock()
337375
config = FakeConfig()
338376

@@ -341,6 +379,7 @@ def test_record_elastic_event_start_scale_up(self):
341379
fake_recorder.record_custom_badput_event_start_time.assert_called_once_with(
342380
custom_badput_event_type="elastic_scale_up"
343381
)
382+
fake_recorder.record_elastic_slice_counts.assert_called_once()
344383

345384
def test_record_elastic_wait_end_and_reinit_start_noop_on_first_attempt(self):
346385
"""Tests recording elastic event end and elastic reinit start."""
@@ -355,6 +394,10 @@ def test_record_elastic_wait_end_and_reinit_start_noop_on_first_attempt(self):
355394

356395
def test_record_elastic_wait_end_and_reinit_start(self):
357396
"""Test recording end of slice down and start of reinit."""
397+
elastic_utils.pending_elastic_event_type = 'elastic_slice_down'
398+
elastic_utils.elastic_manager = self.fake_manager
399+
self.fake_manager.active_slice_indices = {0}
400+
self.fake_manager.slice_to_devices = {0: [FakeDevice()], 1: [FakeDevice()]}
358401
elastic_utils.pending_elastic_event_type = "elastic_slice_down"
359402
fake_recorder = Mock()
360403

@@ -366,19 +409,24 @@ def test_record_elastic_wait_end_and_reinit_start(self):
366409
fake_recorder.record_custom_badput_event_start_time.assert_called_once_with(
367410
custom_badput_event_type="elastic_reinitialization"
368411
)
412+
fake_recorder.record_elastic_slice_counts.assert_called_once()
369413
self.assertIs(elastic_utils.pending_reinit_recorder, fake_recorder)
370414
self.assertIsNone(elastic_utils.pending_elastic_event_type)
371415

372416
def test_record_elastic_reinit_end(self):
373417
"""Tests recording end of elastic reinit."""
374418
fake_recorder = Mock()
375419
elastic_utils.pending_reinit_recorder = fake_recorder
420+
elastic_utils.elastic_manager = self.fake_manager
421+
self.fake_manager.active_slice_indices = {0}
422+
self.fake_manager.slice_to_devices = {0: [FakeDevice()], 1: [FakeDevice()]}
376423

377424
elastic_utils.record_elastic_reinit_end()
378425

379426
fake_recorder.record_custom_badput_event_end_time.assert_called_once_with(
380427
custom_badput_event_type="elastic_reinitialization"
381428
)
429+
fake_recorder.record_elastic_slice_counts.assert_called_once()
382430
self.assertIsNone(elastic_utils.pending_reinit_recorder)
383431

384432
def test_record_elastic_reinit_end_on_cold_start(self):
@@ -401,6 +449,8 @@ def __setattr__(self, name, value):
401449

402450
config = ReadOnlyConfig()
403451
self.fake_pathwaysutils.is_pathways_backend_used.return_value = True
452+
self.fake_pathwaysutils.elastic = Mock()
453+
self.fake_pathwaysutils.elastic.get_active_slice_indices.return_value = [0, 1]
404454

405455
# Should not raise ValueError
406456
elastic_utils.ensure_elastic_manager_initialized(config)

0 commit comments

Comments
 (0)