pytorch api文档:张量的 .contiguous()方法-返回内存连续的新张量

介绍

张量的 .contiguous() 方法,是解决 张量内存不连续 问题的核心

连续张量和非连续张量

首先要理解张量在内存中的存储逻辑:

  • PyTorch 张量的元素在内存中是一维线性存储的,“维度”只是对这段线性内存的“解读方式”;
  • 连续张量:元素的内存顺序与按维度遍历(如按行)的顺序完全一致(比如 [2,3] 的张量,内存中是 1,2,3,4,5,6,按行遍历也是这个顺序);
  • 非连续张量:元素的内存顺序与维度遍历顺序不一致(如转置后的张量,维度遍历是 1,4,2,5,3,6,但内存中还是 1,2,3,4,5,6)。

.contiguous() 作用

返回一个与原张量 数据相同、维度相同,但内存连续 的新张量(若原张量已连续,则返回自身,无额外拷贝)。

关键特征

  • 若原张量已连续:返回原张量(无拷贝,高效);
  • 若原张量非连续:创建新的连续内存副本(数据与原张量一致,不再共享内存)。

语法

Tensor.contiguous(memory_format=torch.contiguous_format) → Tensor

参数:

  • memory_format 可选,默认 torch.contiguous_format(行优先连续),还支持 torch.channels_last(通道优先,用于CV优化);

例子

结合 .transpose() 和 .view(),演示 .contiguous() 的必要性:

import torch

# 1. 初始张量:连续的
x = torch.tensor([[1, 2, 3], 
                  [4, 5, 6]])
print("原张量是否连续:", x.is_contiguous())  # True
print("原张量内存顺序(展平):", x.view(-1))  # [1,2,3,4,5,6]

# 2. 转置后:非连续张量
x_trans = x.transpose(0, 1)
print("\n转置后是否连续:", x_trans.is_contiguous())  # False
# x_trans.view(-1)  # 直接调用view会报错!报错信息:RuntimeError: view size is not compatible with input tensor's size and stride

# 3. 用.contiguous()整理内存:变为连续张量
x_trans_contig = x_trans.contiguous()
print("contiguous后是否连续:", x_trans_contig.is_contiguous())  # True
print("contiguous后view展平:", x_trans_contig.view(-1))  # [1,4,2,5,3,6]

# 4. 内存拷贝验证:contiguous()后不再共享内存
x_trans_contig[0, 0] = 100
print("\n修改contiguous后的张量:")
print(x_trans_contig)  # [[100,4],[2,5],[3,6]]
print("原转置张量(未变):")
print(x_trans)  # [[1,4],[2,5],[3,6]]
print("原始张量(未变):")
print(x)  # [[1,2,3],[4,5,6]]

# 5. 已连续张量调用contiguous():返回自身(无拷贝)
x_contig = x.contiguous()
print("\n原张量与contiguous后张量是否同一对象:", x is x_contig)  # True

输出核心特征:

  • 转置后的张量非连续,直接用 .view() 会报错,必须先 .contiguous();
  • .contiguous() 会创建内存副本(非连续张量时),新张量与原张量不再共享内存;
  • 已连续的张量调用 .contiguous() 无开销,直接返回自身。

为什么需要 .contiguous()

PyTorch 的很多核心操作(如 .view()、某些算子)要求张量内存连续,原因:

  1. .view() 的底层限制:.view() 只是“重新解读”内存,无法处理内存顺序与维度遍历顺序不一致的情况,必须依赖连续内存才能正确重塑形状;
  2. 性能优化:连续张量的内存访问是线性的,速度远快于非连续张量(非连续张量会跳着读取内存,触发缓存未命中);
  3. 算子兼容性:部分 CUDA 算子/底层优化仅支持连续张量,非连续张量会导致报错或性能下降。

常见触发张量非连续的操作

以下操作会让张量从连续变为非连续,后续若用 .view() 需先 .contiguous():

  • 维度交换:.transpose()、.permute();
  • 切片/步长:x[:, ::2](隔列取数)、x[1:];
  • 转置相关:.t()、.mT;
  • 其他:.unfold()、某些 padding 操作。

什么时候用

不用无脑加,遵循以下原则即可:

  1. 必加场景:对非连续张量调用 .view() 前(可先用 x.is_contiguous() 判断);
  2. 建议加场景:对非连续张量做大量计算前(提升内存访问效率);
  3. 无需加场景:
    • 张量本身已连续(如刚创建的张量、.clone() 后的张量);
    • 仅做简单运算(如加减乘除),无需重塑形状。

总结

  1. .contiguous() 用于将非连续张量转为内存连续的新张量,是 .view() 处理非连续张量的必要前置操作;
  2. 非连续张量通常由 .transpose()/.permute()/切片等操作触发,核心特征是内存顺序与维度遍历顺序不一致;
  3. .contiguous() 仅在张量非连续时创建内存副本,连续时无开销,使用时优先判断 is_contiguous() 可避免不必要的拷贝。

原文出处:/show_1GW2dNGybasI.html