|
11 | 11 | from torch.fx import Graph, GraphModule |
12 | 12 |
|
13 | 13 | import deepspeed.compile.util as compile_util |
| 14 | +from deepspeed.compile import backend as backend_mod |
14 | 15 | from deepspeed.compile import inductor as inductor_mod |
15 | 16 | from deepspeed.compile import list_schedule as schedule_mod |
16 | 17 | from deepspeed.compile.passes import prefetch as prefetch_mod |
@@ -66,6 +67,31 @@ def _placeholder(graph, name): |
66 | 67 | return _with_meta(graph.placeholder(name)) |
67 | 68 |
|
68 | 69 |
|
| 70 | +def test_sync_memory_profile_complete_noops_without_distributed(monkeypatch): |
| 71 | + monkeypatch.setattr(backend_mod.dist, "is_initialized", lambda: False) |
| 72 | + |
| 73 | + def fail_all_reduce(*args, **kwargs): |
| 74 | + raise AssertionError("all_reduce should not run without distributed init") |
| 75 | + |
| 76 | + monkeypatch.setattr(backend_mod.dist, "all_reduce", fail_all_reduce) |
| 77 | + |
| 78 | + assert backend_mod._sync_memory_profile_complete(True) |
| 79 | + assert not backend_mod._sync_memory_profile_complete(False) |
| 80 | + |
| 81 | + |
| 82 | +def test_sync_memory_profile_complete_reduces_asymmetric_failure(monkeypatch): |
| 83 | + monkeypatch.setattr(backend_mod.dist, "is_initialized", lambda: True) |
| 84 | + monkeypatch.setattr(backend_mod, "get_accelerator", lambda: SimpleNamespace(current_device=lambda: "cpu")) |
| 85 | + |
| 86 | + def mark_any_rank_failed(tensor, op): |
| 87 | + assert op == backend_mod.dist.ReduceOp.MIN |
| 88 | + tensor[0] = 0 |
| 89 | + |
| 90 | + monkeypatch.setattr(backend_mod.dist, "all_reduce", mark_any_rank_failed) |
| 91 | + |
| 92 | + assert not backend_mod._sync_memory_profile_complete(True) |
| 93 | + |
| 94 | + |
69 | 95 | def _allgather(graph, arg, ds_id, name, tensor_size=1, device_time=1): |
70 | 96 | return _with_meta( |
71 | 97 | graph.call_function(torch.ops.dc.allgather_param.default, (arg, 0, ds_id), {"dtype": torch.float16}, |
@@ -301,6 +327,42 @@ def available_memory(self): |
301 | 327 | assert any("incomplete profiling data" in message for message in logs) |
302 | 328 |
|
303 | 329 |
|
| 330 | +def test_schedule_prefetch_skips_when_memory_profile_incomplete(monkeypatch): |
| 331 | + graph = Graph() |
| 332 | + |
| 333 | + param = _placeholder(graph, "mem_incomplete_param") |
| 334 | + ag = _allgather(graph, param, 91, "mem_incomplete") |
| 335 | + wait = _wait(graph, ag, 91, "mem_incomplete") |
| 336 | + use = _neg(graph, wait, "mem_incomplete_use") |
| 337 | + release = _release(graph, use, 91, "mem_incomplete") |
| 338 | + |
| 339 | + graph.output((release, )) |
| 340 | + graph.lint() |
| 341 | + |
| 342 | + profiling_results = { |
| 343 | + 0: |
| 344 | + ProfilingResult(fwd_graph=graph, |
| 345 | + bwd_graph=None, |
| 346 | + fwd_mem=[("profiled_before_abort", 0, 0, 0)], |
| 347 | + fwd_mem_complete=False) |
| 348 | + } |
| 349 | + gm = GraphModule(torch.nn.Module(), graph) |
| 350 | + logs = [] |
| 351 | + |
| 352 | + monkeypatch.setattr(prefetch_mod, "print_rank_0", lambda message: logs.append(message)) |
| 353 | + |
| 354 | + assert prefetch_mod.schedule_prefetch(gm, |
| 355 | + graph_id=0, |
| 356 | + graph_order=[(0, False)], |
| 357 | + profiling_results=profiling_results, |
| 358 | + create_inputs_fn=lambda: (), |
| 359 | + mem_budget=0, |
| 360 | + param_manager={}, |
| 361 | + bwd=False) is gm |
| 362 | + assert gm.graph is graph |
| 363 | + assert any("incomplete profiling data" in message for message in logs) |
| 364 | + |
| 365 | + |
304 | 366 | def test_graphsafe_rng_state_outputs_are_registered_no_reuse(): |
305 | 367 | graphsafe_run_with_rng_state = inductor_mod._get_graphsafe_run_with_rng_state() |
306 | 368 | if graphsafe_run_with_rng_state is None: |
|
0 commit comments