Fix regional compilation not executing under mixed precision - #3147
Conversation
`prepare_model` installs its mixed-precision `forward` as an instance attribute bound to the module it was handed; `compile_regions` copies that binding onto its twin. The twin re-enters the uncompiled original, so regional compilation never runs and gradient checkpointing set after `prepare()` is inert. Re-bind `forward` and `_original_forward` onto the module `prepare` returned. The defect is accelerate's; this helper is a no-op once accelerate re-binds the twin itself.
|
@copilot is this a single gpu only issue or impacted ddp as well? regional compile definitely changes performance in testing on multigpu systems. this is confusing me |
|
DDP is affected the same way, one level down, and this PR's helper does not reach it; huggingface/accelerate#4188 does. Why (accelerate 1.14.0 line numbers): Reproducer — 2 processes, no data or checkpoint (CPU box: gloo, GPUs: NCCL). import torch, torch._dynamo
from torch import nn
from accelerate import Accelerator
from accelerate.utils import TorchDynamoPlugin
class Block(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(4, 4, bias=False)
def forward(self, x):
return self.linear(x)
class Tiny(nn.Module):
def __init__(self):
super().__init__()
self.blocks = nn.ModuleList([Block(), Block()])
def forward(self, x):
for b in self.blocks:
x = b(x)
return x
accelerator = Accelerator(
cpu=not torch.cuda.is_available(),
mixed_precision="bf16",
dynamo_plugin=TorchDynamoPlugin(backend="inductor", use_regional_compilation=True),
)
original = Tiny()
prepared = accelerator.prepare(original)
inner = prepared.module # DDP wrapper -> inner twin
torch._dynamo.utils.counters.clear()
prepared(torch.ones(1, 4, device=accelerator.device))
print(
f"rank {accelerator.process_index} | {type(prepared).__name__} "
f"| inner forward bound to ORIGINAL: {inner.__dict__['forward'].__self__ is original} "
f"| blocks[0]={type(inner.blocks[0]).__name__} "
f"| dynamo frames={dict(torch._dynamo.utils.counters['frames'])}"
)torch 2.14.0.dev; same result on CPU (gloo) and with Trainer (
Regional compile changing multi-GPU performance is consistent with FSDP2/DeepSpeed ( If you want DDP covered here rather than waiting on accelerate, I can extend the helper to walk |
|
did an Accelerate update break this or something? because running tests with regional compile just a few months back (or maybe it's been a year, time flies) was materially changing vram consumption (often for the worse, which this would explain) and performance, but not as much as i'd expect. still, sometimes i'd see the ~30% gain in speed - this is without deepspeed or fsdp2 enabled. |
Under DDP prepare() returns the DistributedDataParallel wrapper, which has no instance forward; the stale binding sits on the inner twin one level down, so the top-level rebind was a no-op there. Walk `.module` wrappers and rebind at each level that still points at the original. Two gloo DDP tests cover the rebind and the no-op once accelerate rebinds the twin itself.
|
This bug has existed since accelerate v1.7.0, where regional compilation first shipped (huggingface/accelerate#3529): PR status: the first commit ( Verified, 2 × B200, bf16
|
|
did you single bf16 out because certain quant methods bypass the issue somehow? for what it's worth, i was trying compile with eg. sdnq and torchao to fuse their compute paths via triton kernels. but i won't push back on merging this one in, it's just that historically it's not been clear where we have to unwrap_model and when operations are done in-place. the fact that Accelerate treats deepspeed and fsdp2 in-place while DDP and single GPU get a twin model is really annoying of Accelerate to have done. |
|
bf16 is singled out because it is what installs the instance-bound
Measured with the trainer, upstream
Bug rows run the original: nothing compiled, VRAM at the checkpointing-off level; this branch ( On the twin: agreed. accelerate#4188 makes |
Summary
dynamo_use_regional_compilation: trueundermixed_precisionother thannobuilds a compiledmodel that never executes; the uncompiled one runs instead.
gradient_checkpointing: truechanges nothing.prepare()returned. It affects everymodel family; the FSDP2 and DeepSpeed prepare paths are not.
Root cause
Accelerator.prepare_modelbinds the autocast wrapper as an instance attribute on its argument(
accelerator.py:1818-1829), then compiles (:2064).compile_regions(
utils/other.py:106-175) builds a twin withnew_module.__dict__.update(module.__dict__),replacing only
_modules, so the copiedforwardstays bound to the pre-compile module, whichthe twin re-enters.
Trigger:
mixed_precision != "no"withdynamo_use_regional_compilation: true. Unaffected: FSDP2and DeepSpeed, whose
compile_regions_*helpers (utils/other.py:178-225) compile in place andbuild no twin.
Changes
helpers/training/wrappers.py:rebind_prepared_forwardre-bindsforwardand_original_forwardin place on the modulepreparereturned, only where they still point atthe original; DDP, FSDP,
torch.compile,mixed_precision: noand no-dynamo are untouched.trainer.py:4577and:4640, the two sites that store a prepared model.prepare_model(evaluation_mode=True)builds the same twin; without the rebind, the.eval()below would leave the executing module at
training=True.Validation
.venv/bin/python -m unittest -v -f tests.test_wrappers_rebind— 9 tests driven by accelerate'sown
compile_regionsand the neighbouring wrapper shapes, six of them no-ops.632d9595fcheckpointing on and off both peak at65,378 MiB, 0 inductor artifacts; with the fix, checkpointing on peaks at 30,496 MiB, writes
270, and runs 0.4667 s/it after warm-up against the same tree's 0.600 with compilation off.
step_loss0.0481.Notes
forward, so a no-op) and the text-encoderprepareattrainer.py:4615.