NumPy/PyTorch/C char数组内存排布

1. 关于 np.random.randn(2, 3) 的数据存储

  • 数据类型 (Data Type)np.random.randn 默认生成的是 64位(8字节)双精度浮点数 (numpy.float64)。所以每个数字占 8个字节,而不是8位(1字节)。这是一个关键区别。

  • 内存布局 (Memory Layout) :默认情况下,NumPy数组使用 C-style (row-major) 的顺序 在内存中紧凑地(contiguously) 存储数据。

对于一个 (2, 3) 的矩阵,其内存布局如下所示:

text

复制代码
[ [a, b, c], [d, e, f] ]

在内存中的排列是连续的:a -> b -> c -> d -> e -> f。每个元素紧挨着下一个元素,中间没有空隙。

所以,是的,np.random.randn(2, 3) 创建的数据是像C语言数组一样,以行主序、紧凑的方式存放在系统内存中的,每个元素占8字节。

可以通过以下属性验证:

python

复制代码
import numpy as np

arr = np.random.randn(2, 3)
print(arr.dtype)      # 输出:float64
print(arr.itemsize)   # 输出:8 (每个元素占8字节)
print(arr.flags)
# 输出中会看到:
#   C_CONTIGUOUS : True  (C风格连续)
#   F_CONTIGUOUS : False (Fortran风格不连续)
#   OWNDATA : True       (数组拥有自己的数据)

2. 关于数据转换时的重新排布

答案是:大多数情况下会,但这取决于转换的源和目标。 核心在于 内存布局的连续性数据类型的匹配

情况一:NumPy数组之间的转换(例如视图 vs. 拷贝)
  • arr.astype(np.float32):这会重新排布 。它创建了一个全新的数组 ,分配了新的内存,并将原float64数据逐个转换为float32再存入。新老数组内存不共享。

  • arr.view(np.float32):这创建的是一个视图 。它不会重新排布原始float64数据的字节,而是用新的数据类型(float32)去解释 同一块内存。因为float32是4字节,所以一个float64(8字节)会被解释成两个float32数,结果通常是无意义的数据。这很危险,但速度快,不拷贝数据。

  • arr.T(转置):对于C连续的数组,转置操作默认返回一个视图 ,但它的内存布局不再是C连续的(变成了F连续的)。访问它可能会更慢,但并没有发生数据拷贝和重新排布。如果你调用 arr.T.copy(),则会强制进行拷贝和重新排布,得到一个C连续的新数组。

情况二:NumPy 与 PyTorch Tensor 的转换

这是非常常见且容易引起性能问题的场景。

  • torch.from_numpy(numpy_arr)

    • 这是最高效的方式 。PyTorch 和 NumPy 可以共享底层内存(前提是都在CPU上,且数据类型兼容)。

    • PyTorch Tensor 会直接使用 NumPy 数组的底层数据缓冲区,不会重新排布或拷贝数据

    • 重要条件 :NumPy数组必须是紧凑连续 的。如果NumPy数组是不连续的(例如,通过切片arr[:, ::2]得到的),torch.from_numpy会失败或被迫拷贝数据。

    • 共享内存意味着,修改一个会影响另一个。

  • torch.tensor(numpy_arr)

    • 这个操作总是会拷贝数据。它会分配新的PyTorch内存,并将NumPy数组的数据复制过去。

    • 即使用于紧凑连续的数组,它也会拷贝。这是为了确保新Tensor完全独立于原来的NumPy数组。

结论:在NumPy和PyTorch间转换时,为了效率应优先使用 torch.from_numpy 并确保NumPy数组是连续的。如果不需要共享内存,则用 torch.tensor

情况三:与C语言char数组的转换

这通常涉及序列化/反序列化与底层C代码交互

  • 从C char数组到NumPy/PyTorch

    • 如果你有一个C char数组(本质是一段原始的字节缓冲区void* + 长度),并且你知道这段内存的数据类型和形状,你可以让NumPy/PyTorch直接"接管"这段内存

    • NumPy : np.frombuffernp.ndarray 的构造函数。可以创建一个视图 ,将字节缓冲区解释为指定数据类型和形状的数组。不重新排布数据,零拷贝

    • PyTorch : torch.frombuffer (较新版本) 或 torch.from_numpy(np.frombuffer(...))。同样旨在实现零拷贝。

    • 风险:你必须绝对保证C数组的内存布局(字节顺序、连续性)与你要创建的数组的要求完全匹配,否则数据解释会是错误的。

  • 从NumPy/PyTorch到C char数组

    • 本质上就是获取数组底层数据缓冲区的指针。

    • NumPy : arr.dataarr.__array_interface__['data'][0]

    • PyTorch : tensor.data_ptr()

    • 你可以将这个指针传递给C函数,C函数就可以直接读写这块内存。同样,前提是Tensor在内存中是紧凑连续的,否则C代码访问到的数据布局会和预期不符。


总结

操作 是否会重新排布/拷贝数据? 说明
np.random.randn(2,3) 创建紧凑、C连续的float64数组
arr.astype(new_dtype) 创建新数组,拷贝并转换数据
arr.view(new_dtype) 创建视图,重新解释原有数据(危险)
arr.T 创建转置视图,但布局可能改变
torch.from_numpy(arr) 通常否 零拷贝共享内存 ,要求arr连续
torch.tensor(arr) 总是拷贝数据,创建独立Tensor
与C数组互转 通常否 通过np.frombuffer/torch.frombuffer或直接获取指针,零拷贝,但对内存布局有严格要求

核心思想 :高性能计算库(NumPy, PyTorch)在与自身或其他库交互时,会尽可能地避免数据拷贝 (零拷贝),而是通过共享内存来实现高效操作。能否实现零拷贝的关键在于内存布局(尤其是连续性)和数据类型的兼容性。如果布局或类型不匹配,框架就不得不进行昂贵的数据拷贝和重新排布。

相关推荐
深度学习入门7 小时前
如何使用PyTorch搭建一个基础的神经网络并进行训练?
人工智能·pytorch·python·深度学习·神经网络·ai
纵有疾風起9 小时前
数据结构——二叉树
c语言·数据结构·算法·链表
云烟成雨TD9 小时前
NumPy 2.x 完全指南【三十二】通用函数(ufunc)之数学运算函数
python·机器学习·numpy
深兰科技11 小时前
柳州市委常委、统战部部长,副市长潘展东率队首访深兰科技集团新总部,共探 AI 赋能制造大市与东盟合作新局
人工智能·beautifulsoup·numpy·pyqt·matplotlib·pygame·深兰科技
小莞尔11 小时前
【51单片机】【protues仿真】基于51单片机智能晾衣架系统
c语言·stm32·单片机·嵌入式硬件·51单片机
蓝风破云13 小时前
模拟实现STL中的list容器
c语言·数据结构·c++·链表·迭代器·list·iterator
Peter_Deng.14 小时前
C语言 - 输出参数详解:从简单示例到 alloc_chrdev_region
c语言·开发语言
麦麦在写代码15 小时前
联合体Union
c语言
Stara051116 小时前
基于Ultralytics YOLO通用目标检测训练体系与PyTorch EfficientNet的图像分类体系实现
pytorch·深度学习·神经网络·yolo·目标检测·计算机视觉·迁移学习