ndarray 的内部内存布局#
ndarray 类的一个实例由一个连续的一维计算机内存段(由数组或其他对象拥有)组成,并结合一个将 N 个整数映射到该块中项位置的索引方案。索引可以变化的范围由数组的 shape 指定。每个项占用多少字节以及字节如何被解释由与数组关联的 数据类型对象 定义。
内存段本质上是一维的,并且有许多不同的方案可以将 N 维数组的项排列在一维块中。NumPy 非常灵活,ndarray 对象可以适应任何跨步索引方案。在跨步方案中,N 维索引 \((n_0, n_1, ..., n_{N-1})\) 对应于与数组关联的内存块起始位置的偏移量(以字节为单位)
\[n_{\mathrm{offset}} = \sum_{k=0}^{N-1} s_k n_k\]
从内存块的开始处。这里,\(s_k\) 是指定数组 跨步 的整数。 列主序(例如,在 Fortran 语言和 Matlab 中使用)和 行主序(在 C 中使用)方案只是跨步方案的特定类型,并且对应于可以通过跨步 寻址 的内存
\[s_k^{\mathrm{column}} = \mathrm{itemsize} \prod_{j=0}^{k-1} d_j , \quad s_k^{\mathrm{row}} = \mathrm{itemsize} \prod_{j=k+1}^{N-1} d_j .\]
其中 \(d_j\) = self.shape[j]。
C 风格和 Fortran 风格的连续数组都是连续的,即单段内存布局,其中内存块的每一部分都可以通过索引的某种组合来访问。
注意
连续数组和单段数组是同义词,在文档中可互换使用。
虽然具有相应标志的 C 风格和 Fortran 风格连续数组可以通过上述跨步进行寻址,但实际的跨步可能不同。这种情况可能在两种情况下发生
如果 self.shape[k] == 1,则对于任何合法的索引 index[k] == 0。这意味着在偏移量公式中 \(n_k = 0\),因此 \(s_k n_k = 0\),并且 \(s_k\) = self.strides[k] 的值是任意的。
如果数组没有元素(self.size == 0),则没有合法的索引,并且永远不会使用跨步。任何没有元素的数组都可以被认为是 C 风格和 Fortran 风格的连续数组。
第一点意味着 self 和 self.squeeze() 始终具有相同的连续性和 aligned 标志值。这也意味着即使是高维数组也可以同时是 C 风格和 Fortran 风格的连续数组。
当内存偏移量和基址偏移量都是 self.itemsize 的倍数时,数组被认为是已对齐的。理解内存对齐可以提高大多数硬件上的性能。
警告
对于 C 风格连续数组,通常不满足 self.strides[-1] == self.itemsize;对于 Fortran 风格连续数组,通常不满足 self.strides[0] == self.itemsize。
新 ndarray 中的数据默认为行主序(C)顺序,除非另有说明,但例如,基本数组切片 通常会产生不同方案的视图。
注意
NumPy 中的许多算法都作用于任意跨步数组。然而,有些算法需要单段数组。当将一个跨步不规则的数组传递给此类算法时,会自动创建一个副本。