Skip to content

Conversation

zasdfgbnm
Copy link
Collaborator

@zasdfgbnm zasdfgbnm commented Nov 11, 2022

Select op takes a slice of a tensor at a given dimension. For example, for a 3D tensor, t.select(1, 123) means taking a slice along dim 1 at index 123, which is identical to t[:, 123, :].

The reason why I write this PR is:

  • select is a commonly used op in PyTorch, it is always good to have better op coverage.
  • It is similar to index_select but easier, so this PR could serve as a reference for the index_select, and it is also a good place to discuss indexing about index_select here.

The current support of select is not complete, it has restrictions that require the input tensor to be a fusion input. And I don't think in registry.cpp, this PR is detecting all unsupported cases and rejecting them. But for now, I believe providing this level of support is sufficient.

@zasdfgbnm zasdfgbnm marked this pull request as draft November 11, 2022 07:56
@zasdfgbnm zasdfgbnm marked this pull request as ready for review November 11, 2022 08:58
@zasdfgbnm zasdfgbnm requested a review from naoyam November 11, 2022 08:58
Copy link
Collaborator

@naoyam naoyam left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for adding the support of select.

@@ -1615,7 +1612,10 @@ std::vector<Val*> Index::getGlobalProducerStridedIndices(
loops,
root_ind);

root_ind = getProducerIndexWithHalo(producer_tv, i, root_ind, consumer_tv);
if (root_dom[i] != selected_id) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should just assert this for now. getProducerIndexWithHalo should just return root_ind if the root domain is not extended with halo nor accessed through a shift op.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There is an assert inside getProducerHaloOffset:

  auto it = p2c.find(producer_id);
  // p2c should always have a mapping for producer_id. The only case
  // where no mapping exists for a producer axis is when it is a
  // reduction axis. Since this function is only used for indexing
  // producer tensors, where reduction axes are skipped, producer_id
  // should never be a reduction axis.
  TORCH_INTERNAL_ASSERT(it != p2c.end());

which fails for the select id. Do we want to remove this assert and return 0 if not mapped?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, I think we should remove the assert, but I'd feel more assured with some validation that the producer ID is a selected ID.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I moved the check inside getProducerIndexWithHalo, something like

if (override) {
  offset = 0;
} else {
  assert 
}

@zasdfgbnm zasdfgbnm requested a review from naoyam November 11, 2022 20:46
Copy link
Collaborator

@naoyam naoyam left a comment

Choose a reason for hiding this comment

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

LGTM. Added some minor comments.

@@ -1591,18 +1575,27 @@ std::vector<Val*> Index::getGlobalProducerStridedIndices(
continue;
}

TORCH_INTERNAL_ASSERT(
Val* root_ind = nullptr;
auto override_it = override_index.find(root_dom[i]);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I was thinking about passing the optional map to getTensorIndexFromIdGraph to provide an initial ID-to-index map. That would be more consistent if we would want to allow the same initial map in consumer indexing.

That said, I think this is good enough for now given that the whole indexing code would be redesigned.

Pinging @csarofeen

Copy link
Owner

Choose a reason for hiding this comment

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

Agreed

@@ -1615,7 +1612,10 @@ std::vector<Val*> Index::getGlobalProducerStridedIndices(
loops,
root_ind);

root_ind = getProducerIndexWithHalo(producer_tv, i, root_ind, consumer_tv);
if (root_dom[i] != selected_id) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, I think we should remove the assert, but I'd feel more assured with some validation that the producer ID is a selected ID.

@zasdfgbnm zasdfgbnm merged commit ca1387a into devel Nov 15, 2022
@zasdfgbnm zasdfgbnm deleted the select branch November 15, 2022 07:42
@csarofeen
Copy link
Owner

Is select an alias operation?

@zasdfgbnm
Copy link
Collaborator Author

Is select an alias operation?

It is, it returns a view of the input tensor.

@naoyam
Copy link
Collaborator

naoyam commented Nov 16, 2022

Is select an alias operation?

It is, it returns a view of the input tensor.

In nvFuser, even a view isn't really an alias as it creates a new (transformed) copy. I don't think we have a true alias in nvFuser. Is it different in PyTorch? Is a view a true "view" to another tensor?

@zasdfgbnm
Copy link
Collaborator Author

Is select an alias operation?

It is, it returns a view of the input tensor.

In nvFuser, even a view isn't really an alias as it creates a new (transformed) copy. I don't think we have a true alias in nvFuser. Is it different in PyTorch? Is a view a true "view" to another tensor?

Yes, in eager mode, view is a true view. If the stride is not compatible with the viewed shape, then view will throw an error. For this case, user can use reshape to make a copy of the tensor with compatible stride.

@naoyam
Copy link
Collaborator

naoyam commented Nov 16, 2022

Is select an alias operation?

It is, it returns a view of the input tensor.

In nvFuser, even a view isn't really an alias as it creates a new (transformed) copy. I don't think we have a true alias in nvFuser. Is it different in PyTorch? Is a view a true "view" to another tensor?

Yes, in eager mode, view is a true view. If the stride is not compatible with the viewed shape, then view will throw an error. For this case, user can use reshape to make a copy of the tensor with compatible stride.

Hmm, so I'm assuming we somehow handle the semantic difference, right? I wonder what would happen when writing to view tensors.

@zasdfgbnm
Copy link
Collaborator Author

Is select an alias operation?

It is, it returns a view of the input tensor.

In nvFuser, even a view isn't really an alias as it creates a new (transformed) copy. I don't think we have a true alias in nvFuser. Is it different in PyTorch? Is a view a true "view" to another tensor?

Yes, in eager mode, view is a true view. If the stride is not compatible with the viewed shape, then view will throw an error. For this case, user can use reshape to make a copy of the tensor with compatible stride.

Hmm, so I'm assuming we somehow handle the semantic difference, right? I wonder what would happen when writing to view tensors.

I don't know. I remember seeing something called view_copy, not sure how it works though. Probably TorchScript might convert the IR to SSA before feeding to nvfuser? @jjsjann123

@csarofeen
Copy link
Owner

Yeah, this was why I asked. We would need to figure out how to support the alias analysis of the op to pipe it through.

@jjsjann123
Copy link
Collaborator

sounds like we needed to have an alias analysis to ensure that changing the select op to select_copy preserves correct semantics. We did that for other view like ops.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants