Skip to content

[Feature] Add inplace=True to pad - #1706

Merged
vmoens merged 4 commits into
pytorch:mainfrom
vmoens:inplace-pad
May 18, 2026
Merged

[Feature] Add inplace=True to pad#1706
vmoens merged 4 commits into
pytorch:mainfrom
vmoens:inplace-pad

Conversation

@vmoens

@vmoens vmoens commented May 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds inplace=True to tensordict.pad so it pads each leaf in-place inside the input tensordict and releases each old storage as soon as the replacement is written. Peak memory for td = pad(td, ...)-style usage drops from ~2x the leaves to ~1x.
  • Exposes pad as a method on TensorDictBase (and registers it on tensorclass).
  • LazyStackedTensorDict is handled by padding each constituent along the non-stack dims in place and growing the stack dim by appending/prepending zero-filled copies of the edge constituents — so the lazy stack's identity is preserved even though its batch_size setter forbids resizing.
  • Documents the project's method_ (same-storage) vs inplace=True (same-container) convention in CLAUDE.md and docs/source/overview.rst, and the tensorclass dispatch list registers pad so the fallback warning is no longer triggered.

The motivation: pad(td, ...) currently builds a fresh tensordict via TensorDict._new_unsafe({}, new_batch_size, ...) and populates it leaf-by-leaf, holding the original td alive for the duration of the call. Common usage is td = pad(td, ...) where the caller wants the original released, but the function-local reference keeps it alive until return — peak ~2x. With inplace=True the rebind happens inside the input, so each old leaf is freed before the next leaf's pad allocates.

Test plan

  • pytest test/test_tensordict.py -k pad — 58 passed, 1 unrelated skip
  • pytest test/test_tensordict.py::TestGeneric — 279 passed
  • New tests cover: object identity preservation, leaf-for-leaf equivalence with the non-inplace path, weak-ref-based check that old leaf storages are released, lazy stack pad along non-stack dim, lazy stack pad along the stack dim, lazy stack pad along both, tensorclass dispatch.
  • Manual smoke (in docstring example): td.pad([0, 0, 0, 1], inplace=True) is td returns True.

Follow-ups (not in this PR)

Other shape-changing ops with the same 2x-memory profile — candidates for the same inplace=True treatment in future PRs: gather (already has out=), repeat / repeat_interleave, roll, reshape / flatten / unflatten (only when the underlying call materializes a copy), contiguous, where. None warrant an underscore variant, since they all change shape/dtype/layout.

`pad(td, ...)` currently allocates a fresh tensordict alongside the
input, so peak memory is ~2x the leaves until the caller's reference
to the original drops. With `inplace=True` the function rebinds each
leaf inside the input as it goes, releasing the old storage before
the next leaf's pad runs, and updates the batch_size at the end.
Peak overhead collapses to roughly one leaf's worth.

LazyStackedTensorDict can't have its batch_size mutated directly, so
the inplace path pads each constituent along non-stack dims in place
and grows the stack dim by appending/prepending zero-filled copies of
the edge constituents.

Also adds `td.pad(...)` as a method on TensorDictBase, registers it
on tensorclass, and documents the project's `method_` (same-storage)
vs `inplace=True` (same-container) convention in CLAUDE.md and
docs/source/overview.rst.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label May 18, 2026
@github-actions github-actions Bot added Feature New feature documentation Improvements or additions to documentation Test tensorclass functional and removed Feature New feature labels May 18, 2026
`pad(td, ..., inplace=True)` mutates the input leaf by leaf. If a
later leaf's pad raises, earlier leaves are already padded and the
batch_size still reflects the old shape, leaving the tensordict
inconsistent. A full transactional rollback would require keeping
every old leaf alive until the whole pass succeeds, which would
defeat the 1x memory contract.

This adds a `safe` kwarg (default `True`) that runs a non-mutating
pre-flight walk over every leaf, validating the per-leaf
preconditions of torch.nn.functional.pad (sufficient dims, no
negative output size) and recursing into nested tensordicts and
lazy-stack constituents. Realistic user errors (bad pad widths,
unpaddable leaves) now raise before any mutation. `safe=False`
skips the walk for a small speedup when the inputs are known to be
valid; the docstring warns that OOM-style failures mid-loop still
corrupt the tensordict regardless of `safe`.
@github-actions github-actions Bot added the Feature New feature label May 18, 2026
vmoens added 2 commits May 18, 2026 15:23
test_tensorclass_stub_methods enforces that every public method on
TensorDict has a matching signature in tensorclass.pyi. Add the pad
stub. Bundles a pure-formatting pass from the linter.
@vmoens
vmoens merged commit f7d8efa into pytorch:main May 18, 2026
66 of 69 checks passed
@vmoens
vmoens deleted the inplace-pad branch May 18, 2026 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. documentation Improvements or additions to documentation Feature New feature functional tensorclass Test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant