Skip to content

Convert aten.slice.Tensor to ttnn.slice #179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open

Convert aten.slice.Tensor to ttnn.slice #179

wants to merge 11 commits into from

Conversation

jdh8
Copy link
Contributor

@jdh8 jdh8 commented Sep 11, 2024

Ticket

None

Problem description

  • PyTorch lowers input[:, :, start:end] to 3 aten.slice.Tensor ops for each dimensions. I'd like to eliminate the outer two no-op slicing ops, but I need help from @kevinwuTT.
  • Note that ending indices of ttnn.slice are inclusive

What's changed

Convert aten.slice.Tensor to ttnn.slice in supported cases

@jdh8 jdh8 self-assigned this Sep 11, 2024
@jdh8
Copy link
Contributor Author

jdh8 commented Sep 11, 2024

I'm getting this error message. I test slicing like tensor[:, :, start:end, :]. What are the specs/restrictions of ttnn.slice?

E       RuntimeError: TT_FATAL @ /home/runner/work/tt-metal/tt-metal/ttnn/cpp/ttnn/operations/data_movement/slice/device/slice_op.cpp:91: (output_tensor_shape[-2] % TILE_HEIGHT == 0) && (this->slice_start[-2] % TILE_HEIGHT == 0)
E       info:
E       Can only unpad tilized tensor with full tiles
E       backtrace:
E        --- ttnn::operations::data_movement::SliceDeviceOperation::validate_with_output_tensors(std::__1::vector<tt::tt_metal::Tensor, std::__1::allocator<tt::tt_metal::Tensor>> const&, std::__1::vector<std::__1::optional<tt::tt_metal::Tensor>, std::__1::allocator<std::__1::optional<tt::tt_metal::Tensor>>> const&) const

@jdh8 jdh8 mentioned this pull request Sep 11, 2024
@ayerofieiev-tt
Copy link
Member

I'm getting this error message. I test slicing like tensor[:, :, start:end, :]. What are the specs/restrictions of ttnn.slice?

E       RuntimeError: TT_FATAL @ /home/runner/work/tt-metal/tt-metal/ttnn/cpp/ttnn/operations/data_movement/slice/device/slice_op.cpp:91: (output_tensor_shape[-2] % TILE_HEIGHT == 0) && (this->slice_start[-2] % TILE_HEIGHT == 0)
E       info:
E       Can only unpad tilized tensor with full tiles
E       backtrace:
E        --- ttnn::operations::data_movement::SliceDeviceOperation::validate_with_output_tensors(std::__1::vector<tt::tt_metal::Tensor, std::__1::allocator<tt::tt_metal::Tensor>> const&, std::__1::vector<std::__1::optional<tt::tt_metal::Tensor>, std::__1::allocator<std::__1::optional<tt::tt_metal::Tensor>>> const&) const

I think if the tensor is tilized, you can't slice to non-tile aligned chunks

@sjameelTT
Copy link

I'm getting this error message. I test slicing like tensor[:, :, start:end, :]. What are the specs/restrictions of ttnn.slice?

E       RuntimeError: TT_FATAL @ /home/runner/work/tt-metal/tt-metal/ttnn/cpp/ttnn/operations/data_movement/slice/device/slice_op.cpp:91: (output_tensor_shape[-2] % TILE_HEIGHT == 0) && (this->slice_start[-2] % TILE_HEIGHT == 0)
E       info:
E       Can only unpad tilized tensor with full tiles
E       backtrace:
E        --- ttnn::operations::data_movement::SliceDeviceOperation::validate_with_output_tensors(std::__1::vector<tt::tt_metal::Tensor, std::__1::allocator<tt::tt_metal::Tensor>> const&, std::__1::vector<std::__1::optional<tt::tt_metal::Tensor>, std::__1::allocator<std::__1::optional<tt::tt_metal::Tensor>>> const&) const

I think if the tensor is tilized, you can't slice to non-tile aligned chunks

Yeah try ROW major. You're slicing the outer collapsed dim of 1416=64 to 8, which is not tile-aligned. Try row major for this.

@@ -118,7 +118,8 @@ def is_function_call(node) -> bool:
ttnn.permute,
# ttnn.repeat, in target_wrapper
ttnn.concat,
# ttnn.split, # ttnn has no split, remote the comment in the future when it has
# ttnn.split, # ttnn has no split, remove the comment in the future when it has
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made #184 because the patch is useful on its own. After that, ttnn.split becomes a valid conversion target.

if step != 1 or dim >= len(input_size):
return None

# NOTE(jdh8): is there a way to skip a no-op?
Copy link
Member

Choose a reason for hiding this comment

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

@kevinwuTT is there a way to skip an op if its a no-op?

Copy link
Contributor

Choose a reason for hiding this comment

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

Returning tensor seems fine here. The following code will automatically replace the uses of the aten.split op to point to the arg[0] tensor, which effectively removes this op.

if new_nodes:
node.replace_all_uses_with(
new_nodes[-1],
delete_user_cb=lambda node: node != new_nodes[-1],
)


slice_start[dim] = start
slice_end[dim] = min(end, input_size[dim])
slice_end -= 1
Copy link
Member

Choose a reason for hiding this comment

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

thats because torch is exclusive and ttnn is inclusive, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes! This is exactly my intention, to convert exclusive ends to inclusive ends.

Choose a reason for hiding this comment

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

if you use python style splices (eg) tensor[..., begin:end:step]) then it's exclusive. If you use ttnn.slice() it's inclusive.

Choose a reason for hiding this comment

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

I am going to refactor ttnn.slice to be exclusive

jdh8 added a commit that referenced this pull request Sep 11, 2024
…ent pass

This patch originates from #179 but is good on its own
ayerofieiev-tt added a commit that referenced this pull request Sep 12, 2024
…ent pass (#184)

This patch originates from #179 but is good on its own

Co-authored-by: Artem Yerofieiev <[email protected]>
return None

if start == 0 and end >= input_size[dim]:
return tensor
Copy link
Member

Choose a reason for hiding this comment

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

what is the difference in returning None vs tensor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

None marks failed conversion and leaves the graph as is. Returning a tenor lets the tenor replace the original op.

@ayerofieiev-tt
Copy link
Member

ayerofieiev-tt commented Sep 12, 2024

Running manually here
https://github.com/tenstorrent/pytorch2.0_ttnn/actions/runs/10833885090

Tests failed

@ayerofieiev-tt
Copy link
Member

@jdh8 negative values are now supported with this PR
tenstorrent/tt-metal#12469

So you can improve when this gets merged
#187

@ayerofieiev-tt
Copy link
Member

@jdh8 that PR is now merged.


# NOTE(jdh8): is there a way to skip a no-op?
# if start == 0 and end >= input_size[dim]:
# return tensor
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kevinwuTT, I'd like to eliminate no-op slices, but this doesn't seem to be the right way.

From:

aten.slice.Tensor(tensor, dim, 0, INT64_MAX)

To:

tensor

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess we can just return the tensor as the new node after merging #180. RAUW of the return value is collectively handled.

with g.inserting_before(node):
new_node = rewrite_node(node)
if new_node is not None:
node.replace_all_uses_with(
new_node,
delete_user_cb=lambda node: node != new_node,
)


slice_start[dim] = start
slice_end[dim] = min(end, input_size[dim])
slice_end -= 1
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes! This is exactly my intention, to convert exclusive ends to inclusive ends.

@@ -118,7 +118,8 @@ def is_function_call(node) -> bool:
ttnn.permute,
# ttnn.repeat, in target_wrapper
ttnn.concat,
# ttnn.split, # ttnn has no split, remote the comment in the future when it has
# ttnn.split, # ttnn has no split, remove the comment in the future when it has
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made #184 because the patch is useful on its own. After that, ttnn.split becomes a valid conversion target.

return None

if start == 0 and end >= input_size[dim]:
return tensor
Copy link
Contributor Author

Choose a reason for hiding this comment

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

None marks failed conversion and leaves the graph as is. Returning a tenor lets the tenor replace the original op.

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

Successfully merging this pull request may close these issues.

5 participants