Skip to content

Conversation

jbschlosser
Copy link
Contributor

@jbschlosser jbschlosser commented Dec 15, 2023

Stack from ghstack (oldest at bottom):

Part 1 of implementation for general subclass view fake-ification.

The following functional inverses are currently implemented scatter-style and thus never return views:

  • as_strided_copy_inverse()
  • diagonal_copy_inverse()
  • expand_copy_inverse()
  • select_copy_int_inverse()
  • slice_copy_Tensor_inverse()
  • split_copy_Tensor_inverse()
  • split_with_sizes_copy_inverse()
  • unbind_copy_int_inverse()
  • unfold_copy_inverse()

We need to get actual views for the introduction of reverse view funcs coming next.

Details:

  • Use as_strided() to implement actual view inverses for the above
    • Assumes we're given a mutated_view that is actually part of a bigger storage; this isn't really the case for functionalization
  • Introduce InverseReturnMode enum for customization of functional inverses
    • AlwaysView - always return an actual view; needed for reverse view_funcs()
    • NeverView - always do a copy; useful for certain functionalization use cases (e.g. XLA, executorch)
    • ViewOrScatterInverse - return an actual view in most cases, but prefer scatter inverses when they exist. this avoids the need to implement as_strided() for subclasses, which can be difficult or impossible
  • Make sure functionalization works as before
    • Use ViewOrScatterInverse when reapply_views TLS is True or NeverView otherwise
    • Adds tests to ensure old behavior for above inverses in functionalization

Copy link

pytorch-bot bot commented Dec 15, 2023

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/115893

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit 0ba36a6 with merge base 7b7f11f (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@jbschlosser jbschlosser added the topic: not user facing topic category label Dec 15, 2023
…owing views"


Part 1 of implementation for general [subclass view fake-ification](https://docs.google.com/document/d/1C5taWiplmX7nKiURXDOAZG2W5VNJ2iV0fQFq92H0Cxw).

The following functional inverses are currently implemented scatter-style and thus don't properly respect `reapply_views=True`:
* `as_strided_copy_inverse()`
* `diagonal_copy_inverse()`
* `select_copy_int_inverse()`
* `slice_copy_Tensor_inverse()`
* `split_copy_Tensor_inverse()`
* `split_with_sizes_copy_inverse()`
* `unbind_copy_int_inverse()`
* `unfold_copy_inverse()`

We need `reapply_views=True` to be respected to get actual views for the introduction of reverse view funcs coming next.

Details:
* Use `as_strided()` to implement inverses for the above when `reapply_views=True`
    * Assumes we're given a mutated_view that is actually part of a bigger storage; this isn't really the case for functionalization
* Make sure functionalization works as before
    * Change codegen to unconditionally pass `reapply_views=False` for the above to keep the old behavior

TODO: More tests to make sure all the above are covered. At least some of them are covered in the pre-existing tests.

[ghstack-poisoned]
@jbschlosser jbschlosser requested a review from bdhirsh December 15, 2023 20:13
…owing views"


Part 1 of implementation for general [subclass view fake-ification](https://docs.google.com/document/d/1C5taWiplmX7nKiURXDOAZG2W5VNJ2iV0fQFq92H0Cxw).

The following functional inverses are currently implemented scatter-style and thus don't properly respect `reapply_views=True`:
* `as_strided_copy_inverse()`
* `diagonal_copy_inverse()`
* `expand_copy_inverse()`
* `select_copy_int_inverse()`
* `slice_copy_Tensor_inverse()`
* `split_copy_Tensor_inverse()`
* `split_with_sizes_copy_inverse()`
* `unbind_copy_int_inverse()`
* `unfold_copy_inverse()`

We need `reapply_views=True` to be respected to get actual views for the introduction of reverse view funcs coming next.

Details:
* Use `as_strided()` to implement inverses for the above when `reapply_views=True`
    * Assumes we're given a mutated_view that is actually part of a bigger storage; this isn't really the case for functionalization
* Make sure functionalization works as before
    * Adds `called_by_functionalization` flag to functional inverse signatures
    * Adds tests to ensure old behavior for above inverses **in functionalization** even when `reapply_views=True`

[ghstack-poisoned]
@albanD
Copy link
Collaborator

albanD commented Dec 18, 2023

Adds called_by_functionalization flag to functional inverse signatures

Do you really need to add this flag?
You can check that the underlying storages are the exact same size and that the view's (size, stride, offset) match to ensure that the reapply of views is valid to do.

Not that the reapply_views= might be better named force_reapply_views= as when this is false, it might still be a view.

@jbschlosser
Copy link
Contributor Author

jbschlosser commented Dec 18, 2023

Do you really need to add this flag?
You can check that the underlying storages are the exact same size and that the view's (size, stride, offset) match to ensure that > the reapply of views is valid to do.

Nice, I'd love to avoid this flag :) lemme try this out.

Update: I tried this out but it results in views being returned for functionalization when we actually don't want them. I'm unsure how to get around this without a flag that indicates whether or not we're in a functionalization context. Is there some other proxy we can use to determine this with info we have available?

Not that the reapply_views= might be better named force_reapply_views= as when this is false, it might still be a view.

Hm isn't it the other way around? i.e. for functionalization, it may pass reapply_views=True but a non-view may be returned in certain cases.

@bdhirsh
Copy link
Contributor

bdhirsh commented Dec 18, 2023

@albanD we discussed it a bit here: #115893 (comment): the tldr is that:

(1) functionalization has an existing error check in unfold() to detect a mutations on overlapping memory: before adding that check we had silently wrong results

(2) If you replace all of your views with view-copies (e.g. unfold -> unfold_copy), then you no longer have any mutations to overlapping memory. So that check is actually only correct to error on if we are using reapply_views=True.

What's annoying is that before this PR, unfold_inverse would always return a non-view. We need a way to distinguish between:

(1) functionalization uses reapply_views=True in torch.compile. (executorch and XLA are the only users that use the "reapply_views=False" version, which adds view_copy ops into the graph instead of views).

(2) We now need a way to differentiate between view_inverse calls made by functionalization (which normally reapply_views, but can't in some cases like unfold or slice), and view_inverse calls made by autograd's view_inverse logic (which always wants a view, and is ok with as_strided showing up sometimes.

WDYT?

…owing views"


Part 1 of implementation for general [subclass view fake-ification](https://docs.google.com/document/d/1C5taWiplmX7nKiURXDOAZG2W5VNJ2iV0fQFq92H0Cxw).

The following functional inverses are currently implemented scatter-style and thus don't properly respect `reapply_views=True`:
* `as_strided_copy_inverse()`
* `diagonal_copy_inverse()`
* `expand_copy_inverse()`
* `select_copy_int_inverse()`
* `slice_copy_Tensor_inverse()`
* `split_copy_Tensor_inverse()`
* `split_with_sizes_copy_inverse()`
* `unbind_copy_int_inverse()`
* `unfold_copy_inverse()`

We need `reapply_views=True` to be respected to get actual views for the introduction of reverse view funcs coming next.

Details:
* Use `as_strided()` to implement inverses for the above when `reapply_views=True`
    * Assumes we're given a mutated_view that is actually part of a bigger storage; this isn't really the case for functionalization
* Make sure functionalization works as before
    * Adds `called_by_functionalization` flag to functional inverse signatures
    * Adds tests to ensure old behavior for above inverses **in functionalization** even when `reapply_views=True`

[ghstack-poisoned]
@jbschlosser jbschlosser changed the title Support reapply_views=True for functional inverses on narrowing views Support view returns for functional inverses on narrowing views Dec 20, 2023
@jbschlosser
Copy link
Contributor Author

As discussed offline, I introduced an enum to cover the 3 cases we need (see PR desc for more details).

Copy link
Contributor

@bdhirsh bdhirsh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Did we agree to introduce new [slice|select]_inverse native ops, to make NestedTensor's life easier so it doesn't have to implement as_strided?

@jbschlosser
Copy link
Contributor Author

Did we agree to introduce new [slice|select]_inverse native ops, to make NestedTensor's life easier so it doesn't have to implement as_strided?

I plan to toss these into a follow-up PR as needed, if that's cool with you

…views"


Part 1 of implementation for general [subclass view fake-ification](https://docs.google.com/document/d/1C5taWiplmX7nKiURXDOAZG2W5VNJ2iV0fQFq92H0Cxw).

The following functional inverses are currently implemented scatter-style and thus never return views:
* `as_strided_copy_inverse()`
* `diagonal_copy_inverse()`
* `expand_copy_inverse()`
* `select_copy_int_inverse()`
* `slice_copy_Tensor_inverse()`
* `split_copy_Tensor_inverse()`
* `split_with_sizes_copy_inverse()`
* `unbind_copy_int_inverse()`
* `unfold_copy_inverse()`

We need to get actual views for the introduction of reverse view funcs coming next.

Details:
* Use `as_strided()` to implement actual view inverses for the above
    * Assumes we're given a mutated_view that is actually part of a bigger storage; this isn't really the case for functionalization
* Introduce `InverseReturnMode` enum for customization of functional inverses
    * `AlwaysView` - always return an actual view; needed for reverse view_funcs()
    * `NeverView` - always do a copy; useful for certain functionalization use cases (e.g. XLA, executorch)
    * `ViewOrScatterInverse` - return an actual view in most cases, but prefer scatter inverses when they exist. this avoids the need to implement `as_strided()` for subclasses, which can be difficult or impossible
* Make sure functionalization works as before
    * Use `ViewOrScatterInverse` when reapply_views TLS is True or `NeverView` otherwise
    * Adds tests to ensure old behavior for above inverses **in functionalization**

[ghstack-poisoned]
@jbschlosser jbschlosser requested a review from bdhirsh December 21, 2023 16:41
Copy link
Contributor

@bdhirsh bdhirsh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@jbschlosser
Copy link
Contributor Author

@pytorchbot merge

@pytorch-bot pytorch-bot bot added the ciflow/trunk Trigger trunk jobs on your pull request label Dec 21, 2023
@pytorchmergebot
Copy link
Collaborator

Merge failed

Reason: Not merging any PRs at the moment because there is a merge blocking https://github.com/pytorch/pytorch/labels/ci:%20sev issue open at:
#116273

Details for Dev Infra team Raised by workflow job

@jbschlosser
Copy link
Contributor Author

@pytorchbot merge

@pytorchmergebot
Copy link
Collaborator

Merge started

Your change will be merged once all checks pass (ETA 0-4 Hours).

Learn more about merging in the wiki.

Questions? Feedback? Please reach out to the PyTorch DevX Team

Advanced Debugging
Check the merge workflow status
here

@facebook-github-bot facebook-github-bot deleted the gh/jbschlosser/109/head branch December 25, 2023 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow/trunk Trigger trunk jobs on your pull request Merged topic: not user facing topic category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants