Making a slice contiguous

Hi, community.

I’m trying to find a way to make a slice of a tensor contiguous. One way I tried is the following using permuate.

import torch

x = torch.zeros([16, 8, 1024, 128], device='cuda:0')                
print(x[:,:, 128:256].is_contiguous()) #False


y = torch.zeros([1024, 16, 8, 128], device='cuda:0')              
print(y[128:256].is_contiguous()) #True

z = y.permute(1,2,0,3)
assert z.size()==x.size()
print(z[:,:,128:256].is_contiguous()) #False

Why is the last print False? I confirmed the data_ptr() still the same, so assume no copy has been made. If this is not the right way to get a contiguous slice, can someone share a trick for that?

Thank you!

Hi Thyeros!

Call .contiguous() on the slice in question. If the slice is already contiguous,
it will be a no-op. If not, it will return a contiguous copy of the slice.

Taking a (non-trivial) permutation of a (contiguous) tensor makes it
non-contiguous.

There are two things going on here. Slicing a set of rows (that is, its first
dimension) out of a tensor gives you a contiguous subtensor, while slicing
out a set of columns (or anything other than the first dimension) gives you
a non-contiguous tensor.

This is the key issue.

But you’re trying to fix this issue by using .permute() to make your slice
apply to the first dimension. However, .permute(), itself, renders the tensor
non-contiguous.

The subtensor (slice) you want simply isn’t stored contiguously in memory
so you have to make a contiguous copy of the data to get your contiguous
slice. .contiguous() does this for you. (I wouldn’t call this a trick – it just
makes a brute-force copy.)

Consider:

>>> import torch
>>> torch.__version__
'2.1.2'
>>> t = torch.zeros (4, 4)
>>> t.is_contiguous()
True
>>> t[1:3, :].is_contiguous()                # the two rows in the slice are still contiguous
True
>>> t[:, 1:3].is_contiguous()                # not contiguous -- have to jump over the missing columns
False
>>> t.permute (1, 0).is_contiguous()         # no longer row-major -- not contiguous
False
>>> t[:, 1:3].contiguous().is_contiguous()   # .contiguous() creates a new contiguous copy
True

Best.

K. Frank

Thanks KFrank. Actually, I’m trying to avoid calling “contiguous()” for the slice, as it becomes expensive in my case (which means every slice I create is NOT contiguous()).