| 53 | |
| 54 | |
| 55 | class CheckpointFunction(torch.autograd.Function): |
| 56 | |
| 57 | @staticmethod |
| 58 | def forward(ctx, run_function, preserve_rng_state, *args): |
| 59 | check_backward_validity(args) |
| 60 | ctx.run_function = run_function |
| 61 | ctx.preserve_rng_state = preserve_rng_state |
| 62 | ctx.had_autocast_in_fwd = torch.is_autocast_enabled() |
| 63 | if preserve_rng_state: |
| 64 | ctx.fwd_cpu_state = torch.get_rng_state() |
| 65 | # Don't eagerly initialize the cuda context by accident. |
| 66 | # (If the user intends that the context is initialized later, within their |
| 67 | # run_function, we SHOULD actually stash the cuda state here. Unfortunately, |
| 68 | # we have no way to anticipate this will happen before we run the function.) |
| 69 | ctx.had_cuda_in_fwd = False |
| 70 | if torch.cuda._initialized: |
| 71 | ctx.had_cuda_in_fwd = True |
| 72 | ctx.fwd_gpu_devices, ctx.fwd_gpu_states = get_device_states(*args) |
| 73 | ctx.save_for_backward(*args) |
| 74 | with torch.no_grad(): |
| 75 | outputs = run_function(*args) |
| 76 | return outputs |
| 77 | |
| 78 | @staticmethod |
| 79 | def backward(ctx, *args): |
| 80 | if not torch.autograd._is_checkpoint_valid(): |
| 81 | raise RuntimeError("Checkpointing is not compatible with .grad(), please use .backward() if possible") |
| 82 | inputs = ctx.saved_tensors |
| 83 | # Stash the surrounding rng state, and mimic the state that was |
| 84 | # present at this time during forward. Restore the surrounding state |
| 85 | # when we're done. |
| 86 | rng_devices = [] |
| 87 | if ctx.preserve_rng_state and ctx.had_cuda_in_fwd: |
| 88 | rng_devices = ctx.fwd_gpu_devices |
| 89 | with torch.random.fork_rng(devices=rng_devices, enabled=ctx.preserve_rng_state): |
| 90 | if ctx.preserve_rng_state: |
| 91 | torch.set_rng_state(ctx.fwd_cpu_state) |
| 92 | if ctx.had_cuda_in_fwd: |
| 93 | set_device_states(ctx.fwd_gpu_devices, ctx.fwd_gpu_states) |
| 94 | detached_inputs = detach_variable(inputs) |
| 95 | with torch.enable_grad(), torch.cuda.amp.autocast(ctx.had_autocast_in_fwd): |
| 96 | outputs = ctx.run_function(*detached_inputs) |
| 97 | |
| 98 | if isinstance(outputs, torch.Tensor): |
| 99 | outputs = (outputs,) |
| 100 | torch.autograd.backward(outputs, args) |
| 101 | grads = tuple(inp.grad if isinstance(inp, torch.Tensor) else inp |
| 102 | for inp in detached_inputs) |
| 103 | return (None, None) + grads |
| 104 | |
| 105 | |
| 106 | def checkpoint(function, *args, **kwargs): |
nothing calls this directly
no outgoing calls
no test coverage detected