I have never had to call .contiguous() after .permute().
As I understand it, permute works by changing the strides of the view mechanism. The data isn’t moved at all so if it was contiguous before permuting, then it still is.
The only caveat is if you slice the tensor after permuting the dimensions. Depending on the original data layout, slicing can result in taking a non-contiguous chunk of data.
More precisely if you take a slice along the first original dimension, then the result will be contiguous. If you take a slice along any other dimension, then the result will be non-contiguous.