torch.Storage#
创建于: 2016年12月30日 | 最后更新于: 2025年04月14日
在 PyTorch 中,常规张量(tensor)是一个多维数组,由以下组件定义:
Storage:张量的实际数据,存储为连续的、一维的字节数组。
dtype:张量中元素的 数据类型,例如 torch.float32 或 torch.int64。shape:一个元组,指示张量在每个维度的大小。Stride:在每个维度中从一个元素移动到下一个元素所需的步长。
Offset:张量数据开始的 Storage 中的起始点。对于新创建的张量,这通常为 0。
这些组件共同定义了张量的结构和数据,其中 Storage 包含实际数据,其余部分作为元数据。
无类型 Storage API#
一个 torch.UntypedStorage 是一个连续的、一维的元素数组。它的长度等于张量的字节数。Storage 作为张量的底层数据容器。通常,在 PyTorch 中使用常规构造函数(如 zeros()、zeros_like() 或 new_zeros())创建的张量,其张量 Storage 与张量本身之间会存在一对一的对应关系。
然而,一个 Storage 允许被多个张量共享。例如,张量的任何视图(通过 view() 或某些(但非全部)索引方式(如整数和切片)获得)将指向与原始张量相同的底层 Storage。在序列化和反序列化共享相同 Storage 的张量时,这种关系会得到保留,并且张量会继续指向相同的 Storage。有趣的是,反序列化指向单个 Storage 的多个张量可能比反序列化多个独立的张量更快。
可以通过 untyped_storage() 方法访问张量 Storage。这将返回一个 torch.UntypedStorage 类型的对象。幸运的是,Storage 有一个通过 torch.UntypedStorage.data_ptr() 方法访问的唯一标识符。在常规情况下,具有相同数据 Storage 的两个张量将具有相同的 Storage data_ptr。然而,张量本身可以指向两个独立的 Storage,一个用于其 data 属性,另一个用于其 grad 属性。每个都需要自己的 data_ptr()。总的来说,不能保证 torch.Tensor.data_ptr() 和 torch.UntypedStorage.data_ptr() 相匹配,不应假定它们会匹配。
无类型 Storage 在某种程度上独立于在其之上构建的张量。实际上,这意味着具有不同 dtype 或 shape 的张量可以指向同一个 Storage。这也意味着一个张量 Storage 可以被修改,如下面的示例所示。
>>> t = torch.ones(3)
>>> s0 = t.untyped_storage()
>>> s0
0
0
128
63
0
0
128
63
0
0
128
63
[torch.storage.UntypedStorage(device=cpu) of size 12]
>>> s1 = s0.clone()
>>> s1.fill_(0)
0
0
0
0
0
0
0
0
0
0
0
0
[torch.storage.UntypedStorage(device=cpu) of size 12]
>>> # Fill the tensor with a zeroed storage
>>> t.set_(s1, storage_offset=t.storage_offset(), stride=t.stride(), size=t.size())
tensor([0., 0., 0.])
警告
请注意,直接修改张量的 Storage(如本示例所示)不推荐使用。这种低级操作仅为教育目的而展示,以说明张量与其底层 Storage 之间的关系。通常,使用标准的 torch.Tensor 方法(如 clone() 和 fill_())来实现相同的结果更有效且更安全。
除了 data_ptr 之外,无类型 Storage 还有其他属性,例如 filename(如果 Storage 指向磁盘上的文件)、device 或 is_cuda 用于设备检查。Storage 也可以使用 copy_、fill_ 或 pin_memory 等方法进行原地或非原地操作。有关更多信息,请参阅下方的 API 参考。请记住,修改 Storage 是一个低级 API,存在风险!其中大部分 API 也存在于张量级别:如果存在,应优先于其 Storage 对应项使用。
特殊情况#
我们提到,一个具有非 None grad 属性的张量实际上在其内部有两个数据块。在这种情况下,untyped_storage() 将返回 data 属性的 Storage,而梯度的 Storage 可以通过 tensor.grad.untyped_storage() 获得。
>>> t = torch.zeros(3, requires_grad=True)
>>> t.sum().backward()
>>> assert list(t.untyped_storage()) == [0] * 12 # the storage of the tensor is just 0s
>>> assert list(t.grad.untyped_storage()) != [0] * 12 # the storage of the gradient isn't
- 还有一些特殊情况,张量没有典型的 Storage,或者根本没有 Storage。
位于
"meta"设备上的张量:位于"meta"设备上的张量用于形状推断,不包含实际数据。Fake Tensors:PyTorch 编译器使用的另一个内部工具是 FakeTensor,它基于相似的理念。
张量子类或类张量对象也可能表现出不寻常的行为。总的来说,我们不认为会有很多用例需要操作 Storage 级别!
- class torch.UntypedStorage(*args, **kwargs)[source]#
-
- copy_()#
- cuda(device=None, non_blocking=False)[source]#
返回此对象在 CUDA 内存中的副本。
如果此对象已在 CUDA 内存中并且位于正确的设备上,则不执行复制,并返回原始对象。
- 参数:
- 返回类型:
_StorageBase | TypedStorage
- data_ptr()#
- element_size()#
- property filename: str | None#
返回与此 Storage 关联的文件名。
如果 Storage 在 CPU 上,并且是通过
from_file()创建的,并且shared设置为True,则文件名将是字符串。否则,此属性为None。
- fill_()#
- static from_buffer()#
- static from_file(filename, shared=False, nbytes=0) Storage#
创建一个由内存映射文件支持的 CPU Storage。
如果
shared为True,则内存将在所有进程之间共享。所有更改都将写入文件。如果shared为False,则对 Storage 的更改不会影响文件。nbytes是 Storage 的字节数。如果shared为False,则文件必须包含至少nbytes字节。如果shared为True,则在需要时会创建文件。(请注意,对于UntypedStorage,此参数与TypedStorage.from_file的参数不同)- 参数:
filename (str) – 要映射的文件名
shared (bool) – 是否共享内存(是否将
MAP_SHARED或MAP_PRIVATE传递给底层 mmap(2) 调用)nbytes (int) – Storage 的字节数
- hpu(device=None, non_blocking=False)[source]#
返回此对象在 HPU 内存中的副本。
如果此对象已在 HPU 内存中并且位于正确的设备上,则不执行复制,并返回原始对象。
- 参数:
- 返回类型:
_StorageBase | TypedStorage
- property is_cuda#
- property is_hpu#
- is_pinned(device='cuda')[source]#
确定 CPU Storage 是否已在 device 上固定。
- 参数:
device (str 或 torch.device) – 要固定内存的设备(默认为
'cuda')。此参数不推荐使用,并可能被弃用。- 返回:
一个布尔变量。
- nbytes()#
- new()#
- pin_memory(device='cuda')[source]#
将 CPU Storage 复制到固定内存,如果它尚未固定。
- 参数:
device (str 或 torch.device) – 要固定内存的设备(默认为
'cuda')。此参数不推荐使用,并可能被弃用。- 返回:
一个固定的 CPU Storage。
- resizable()#
- resize_()#
将 Storage 移动到共享内存。
对于已在共享内存中的 Storage 和 CUDA Storage,此操作无效,因为它们不需要跨进程共享。共享内存中的 Storage 不能调整大小。
请注意,为了缓解 此问题 等问题,从多个线程对同一对象调用此函数是线程安全的。然而,在没有适当同步的情况下对 self 调用任何其他函数不是线程安全的。有关更多详细信息,请参阅 多进程最佳实践。
注意
当共享内存中 Storage 的所有引用都被删除时,关联的共享内存对象也将被删除。PyTorch 有一个特殊的清理过程,以确保即使当前进程意外退出,这种情况也会发生。
值得注意的是
share_memory_()和from_file()(设置shared = True)之间的区别。share_memory_使用 shm_open(3) 创建一个 POSIX 共享内存对象,而from_file()使用 open(2) 打开用户提供的文件名。两者都使用 mmap(2) 调用 和
MAP_SHARED将文件/对象映射到当前虚拟地址空间。share_memory_在映射共享内存对象后会调用shm_unlink(3),以确保当没有进程打开该对象时,共享内存对象会被释放。torch.from_file(shared=True)不会取消链接文件。该文件是持久的,将一直存在,直到用户删除它。
- 返回:
self
- type(dtype=None, non_blocking=False)[源代码]#
- 返回类型:
_StorageBase | TypedStorage
遗留的类型化存储#
警告
出于历史原因,PyTorch 过去使用类型化存储类,这些类现在已弃用,应避免使用。以下详细介绍了此 API,以防您遇到它,尽管强烈不建议使用它。除 torch.UntypedStorage 之外的所有存储类将在未来被移除,并且 torch.UntypedStorage 将在所有情况下使用。
torch.Storage 是与默认数据类型(torch.get_default_dtype())对应的存储类的别名。例如,如果默认数据类型是 torch.float,则 torch.Storage 解析为 torch.FloatStorage。
像 torch.FloatStorage、torch.IntStorage 等 torch.<type>Storage 和 torch.cuda.<type>Storage 类实际上永远不会被实例化。调用它们的构造函数会创建一个具有适当 torch.dtype 和 torch.device 的 torch.TypedStorage。torch.<type>Storage 类拥有 torch.TypedStorage 的所有类方法。
一个 torch.TypedStorage 是一个特定 torch.dtype 的元素的连续一维数组。它可以被赋予任何 torch.dtype,并且内部数据将被正确解释。torch.TypedStorage 包含一个 torch.UntypedStorage,它将数据存储为字节的未类型化数组。
每个跨步 torch.Tensor 都包含一个 torch.TypedStorage,它存储了 torch.Tensor 视图所查看的所有数据。
- class torch.TypedStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[源代码]#
- cuda(device=None, non_blocking=False)[源代码]#
返回此对象在 CUDA 内存中的副本。
如果此对象已在 CUDA 内存中并且位于正确的设备上,则不执行复制,并返回原始对象。
- property device#
- classmethod from_file(filename, shared=False, size=0) Storage[源代码]#
创建一个由内存映射文件支持的 CPU Storage。
如果
shared为True,则内存将在所有进程之间共享。所有更改都将写入文件。如果shared为False,则对 Storage 的更改不会影响文件。size是存储中的元素数量。如果shared为False,则文件必须包含至少size * sizeof(Type)字节(Type是存储类型)。如果shared为True,则在需要时创建文件。- 参数:
filename (str) – 要映射的文件名
shared (bool) –
是否共享内存(是否将
MAP_SHARED或MAP_PRIVATE传递给底层的 mmap(2) 调用)size (int) – 存储中的元素数量
- hpu(device=None, non_blocking=False)[源代码]#
返回此对象在 HPU 内存中的副本。
如果此对象已在 HPU 内存中并且位于正确的设备上,则不执行复制,并返回原始对象。
- property is_cuda#
- property is_hpu#
- is_pinned(device='cuda')[源代码]#
确定 CPU 类型化存储是否已在 device 上固定。
- 参数:
device (str 或 torch.device) – 要固定内存的设备(默认为
'cuda')。此参数不推荐使用,并可能被弃用。- 返回:
一个布尔变量。
- pin_memory(device='cuda')[源代码]#
将 CPU 类型化存储复制到固定内存(如果尚未固定)。
- 参数:
device (str 或 torch.device) – 要固定内存的设备(默认为
'cuda')。此参数不推荐使用,并可能被弃用。- 返回:
一个固定的 CPU Storage。
- type(dtype=None, non_blocking=False)[源代码]#
如果未提供 dtype,则返回类型,否则将此对象转换为指定类型。
如果已是正确的类型,则不执行复制并返回原始对象。
- 参数:
- 返回类型:
_StorageBase | TypedStorage | str
- untyped()[源代码]#
返回内部
torch.UntypedStorage。
- class torch.DoubleStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[源代码]#
- dtype: torch.dtype = torch.float64#
- class torch.FloatStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[源代码]#
- dtype: torch.dtype = torch.float32#
- class torch.HalfStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[源代码]#
- dtype: torch.dtype = torch.float16#
- class torch.LongStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[源代码]#
- dtype: torch.dtype = torch.int64#
- class torch.IntStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.int32#
- class torch.ShortStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.int16#
- class torch.CharStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.int8#
- class torch.ByteStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.uint8#
- class torch.BoolStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.bool#
- class torch.BFloat16Storage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.bfloat16#
- class torch.ComplexDoubleStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.complex128#
- class torch.ComplexFloatStorage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.complex64#
- class torch.QUInt8Storage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.quint8#
- class torch.QInt8Storage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.qint8#
- class torch.QInt32Storage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.qint32#
- class torch.QUInt4x2Storage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.quint4x2#
- class torch.QUInt2x4Storage(*args, wrap_storage=None, dtype=None, device=None, _internal=False)[source]#
- dtype: torch.dtype = torch.quint2x4#