Skip to content

Commit c13ff96

Browse files
authored
Merge pull request #3155 from bghira/fix/reduce-overhead-checkpoint-compat
Handle reduce-overhead with activation checkpointing
2 parents 1176e76 + 956aa3f commit c13ff96

3 files changed

Lines changed: 91 additions & 3 deletions

File tree

simpletuner/helpers/training/dynamo.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
_PEFT_LORA_CUDAGRAPH_PATCHED = False
1313
_PEFT_TE_LORA_CUDAGRAPH_PATCHED = False
14+
_CHECKPOINT_CUDAGRAPH_ISSUE = "https://github.com/pytorch/pytorch/issues/154306"
1415

1516

1617
@torch.compiler.disable
@@ -52,19 +53,43 @@ def run_with_dynamo_config(config: Any, fn: Callable[..., Any], *args: Any, **kw
5253
def _normalise_text(value: Any) -> str:
5354
if value is None:
5455
return ""
56+
enum_value = getattr(value, "value", None)
57+
if isinstance(enum_value, str):
58+
value = enum_value
5559
return str(value).strip().lower().replace("_", "-")
5660

5761

62+
def apply_checkpointing_cudagraph_compatibility(config: Any) -> bool:
63+
backend = _normalise_text(getattr(config, "dynamo_backend", None))
64+
mode = _normalise_text(getattr(config, "dynamo_mode", None))
65+
checkpointing = _coerce_flag(getattr(config, "gradient_checkpointing", False))
66+
if backend != "inductor" or mode not in {"cudagraphs", "reduce-overhead"} or not checkpointing:
67+
return False
68+
69+
config.dynamo_mode = "default"
70+
os.environ["TRAINING_DYNAMO_MODE"] = "default"
71+
os.environ["ACCELERATE_DYNAMO_MODE"] = "default"
72+
logger.warning(
73+
"Activation checkpointing is incompatible with Inductor CUDA Graph mode '%s' due to PyTorch issue %s; "
74+
"using dynamo_mode='default' while preserving regional compilation.",
75+
mode,
76+
_CHECKPOINT_CUDAGRAPH_ISSUE,
77+
)
78+
return True
79+
80+
5881
def _inductor_cudagraphs_enabled(config: Any = None) -> bool:
5982
config_backend = _normalise_text(getattr(config, "dynamo_backend", None))
83+
training_backend = _normalise_text(os.environ.get("TRAINING_DYNAMO_BACKEND"))
6084
accelerate_backend = _normalise_text(os.environ.get("ACCELERATE_DYNAMO_BACKEND"))
61-
backend = accelerate_backend or config_backend
85+
backend = config_backend or training_backend or accelerate_backend
6286
if backend != "inductor":
6387
return False
6488

6589
config_mode = _normalise_text(getattr(config, "dynamo_mode", None))
90+
training_mode = _normalise_text(os.environ.get("TRAINING_DYNAMO_MODE"))
6691
accelerate_mode = _normalise_text(os.environ.get("ACCELERATE_DYNAMO_MODE"))
67-
mode = accelerate_mode or config_mode
92+
mode = config_mode or training_mode or accelerate_mode
6893

6994
try:
7095
import torch._inductor.config as inductor_config

simpletuner/helpers/training/trainer.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@
6969
from simpletuner.helpers.training.deepspeed_optimizers import DEFAULT_OPTIMIZER as DS_DEFAULT_OPTIMIZER
7070
from simpletuner.helpers.training.deepspeed_optimizers import sanitize_optimizer_block
7171
from simpletuner.helpers.training.default_settings.safety_check import safety_check
72-
from simpletuner.helpers.training.dynamo import install_cudagraph_workarounds, mark_cudagraph_step_begin
72+
from simpletuner.helpers.training.dynamo import (
73+
apply_checkpointing_cudagraph_compatibility,
74+
install_cudagraph_workarounds,
75+
mark_cudagraph_step_begin,
76+
)
7377
from simpletuner.helpers.training.evaluation import ModelEvaluator
7478
from simpletuner.helpers.training.exceptions import GPUHealthError
7579
from simpletuner.helpers.training.gpu_circuit_breaker import get_current_gpu_index, get_gpu_circuit_breaker, is_cuda_error
@@ -893,12 +897,16 @@ def _coerce_flag(value: object) -> bool:
893897
except ValueError:
894898
raise
895899

900+
apply_checkpointing_cudagraph_compatibility(self.config)
901+
896902
dynamo_backend_env = "no"
897903
if resolved_dynamo_backend and resolved_dynamo_backend != DynamoBackend.NO:
898904
dynamo_backend_env = resolved_dynamo_backend.value.lower()
899905
elif isinstance(dynamo_backend_value, str) and dynamo_backend_value.strip():
900906
dynamo_backend_env = dynamo_backend_value.strip().lower()
901907
os.environ["TRAINING_DYNAMO_BACKEND"] = dynamo_backend_env
908+
dynamo_mode_env = str(getattr(self.config, "dynamo_mode", "") or "").strip().lower()
909+
os.environ["TRAINING_DYNAMO_MODE"] = dynamo_mode_env
902910
self._configure_inductor_dynamic_training_passes(dynamo_backend_env)
903911
install_cudagraph_workarounds(self.config)
904912

tests/test_dynamo.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,42 @@ def _autograd_graph_contains(fn, needle: str) -> bool:
2020

2121

2222
class DynamoCudagraphWorkaroundTests(unittest.TestCase):
23+
def test_activation_checkpointing_downgrades_reduce_overhead_to_default(self):
24+
from simpletuner.helpers.training import dynamo
25+
26+
config = SimpleNamespace(
27+
dynamo_backend="inductor",
28+
dynamo_mode="reduce-overhead",
29+
dynamo_use_regional_compilation=True,
30+
gradient_checkpointing=True,
31+
)
32+
with (
33+
unittest.mock.patch.object(dynamo.logger, "warning") as warning,
34+
unittest.mock.patch.dict(os.environ, {}, clear=True),
35+
):
36+
self.assertTrue(dynamo.apply_checkpointing_cudagraph_compatibility(config))
37+
self.assertEqual(os.environ["TRAINING_DYNAMO_MODE"], "default")
38+
self.assertEqual(os.environ["ACCELERATE_DYNAMO_MODE"], "default")
39+
40+
self.assertEqual(config.dynamo_mode, "default")
41+
self.assertTrue(config.dynamo_use_regional_compilation)
42+
warning.assert_called_once()
43+
self.assertEqual(warning.call_args.args[2], "https://github.com/pytorch/pytorch/issues/154306")
44+
45+
def test_reduce_overhead_is_preserved_without_activation_checkpointing(self):
46+
from simpletuner.helpers.training import dynamo
47+
48+
config = SimpleNamespace(
49+
dynamo_backend="inductor",
50+
dynamo_mode="reduce-overhead",
51+
gradient_checkpointing=False,
52+
)
53+
with unittest.mock.patch.dict(os.environ, {}, clear=True):
54+
self.assertFalse(dynamo.apply_checkpointing_cudagraph_compatibility(config))
55+
self.assertNotIn("TRAINING_DYNAMO_MODE", os.environ)
56+
57+
self.assertEqual(config.dynamo_mode, "reduce-overhead")
58+
2359
def test_peft_lora_cudagraph_patch_clones_base_result(self):
2460
from peft import LoraConfig
2561
from peft.tuners.lora.layer import Linear
@@ -123,6 +159,25 @@ def test_inductor_cudagraph_tree_mode_enabled_from_accelerate_env(self):
123159
):
124160
self.assertTrue(dynamo._inductor_cudagraphs_enabled(SimpleNamespace(dynamo_backend=None)))
125161

162+
def test_configured_inductor_overrides_outer_accelerate_no_backend(self):
163+
import torch._inductor.config as inductor_config
164+
165+
from simpletuner.helpers.training import dynamo
166+
167+
config = SimpleNamespace(dynamo_backend="inductor", dynamo_mode="reduce-overhead")
168+
with (
169+
unittest.mock.patch.dict(
170+
os.environ,
171+
{
172+
"ACCELERATE_DYNAMO_BACKEND": "NO",
173+
"ACCELERATE_DYNAMO_MODE": "default",
174+
},
175+
),
176+
unittest.mock.patch.object(inductor_config.triton, "cudagraphs", False),
177+
unittest.mock.patch.object(inductor_config.triton, "cudagraph_trees", True),
178+
):
179+
self.assertTrue(dynamo._inductor_cudagraphs_enabled(config))
180+
126181

127182
if __name__ == "__main__":
128183
unittest.main()

0 commit comments

Comments
 (0)