内存对齐#
NumPy 对齐目标#
在NumPy中,与内存对齐相关的使用情况有三种(截至1.14版本):
NumPy 使用两种不同的对齐形式来实现这些目标:”真实对齐”和”Uint 对齐”.
“True” 对齐指的是与C语言中相应C类型的架构依赖对齐.例如,在x64系统中 float64
等同于C中的 double
.在大多数系统上,这具有4或8字节的对齐(这可以通过GCC的选项 malign-double
来控制).如果变量的内存偏移是其对齐的倍数,则该变量在内存中对齐.在某些系统(例如sparc)上,内存对齐是必需的;在其他系统上,它提供了加速.
“Uint” 对齐取决于数据类型的大小.它被定义为 NumPy 复制代码用于复制数据类型的”真实对齐”,或者如果没有等效的 uint,则为未定义/未对齐.目前,NumPy 使用 uint8
、uint16
、uint32
、uint64
和 uint64
分别复制大小为 1、2、4、8、16 字节的数据,所有其他大小的数据类型不能进行 uint 对齐.
例如,在一个(典型的 Linux x64 GCC)系统上,NumPy complex64
数据类型实现为 struct { float real, imag; }
.这具有 4 的”真实”对齐和 8 的”uint”对齐(等于 uint64
的真实对齐).
- 一些情况下 uint 和 true 对齐方式不同(默认 GCC Linux):
arch
类型
true-aln
uint-aln
x86_64
complex64
4
8
x86_64
float128
16
8
x86
float96
4
-
NumPy 中的变量控制和描述对齐方式#
在NumPy中,``align`` 这个词有4种相关的用法:
dtype.alignment
属性(在 C 中为descr->alignment
).这旨在反映类型的”真实对齐”.对于所有数据类型,它具有依赖于架构的默认值,除了使用align=True
创建的结构化类型,如下所述.ndarray 的
ALIGNED
标志,在IsAligned
中计算并通过PyArray_ISALIGNED
检查.这是从dtype.alignment
计算的.如果数组中的每个项目在内存中的位置都与dtype.alignment
一致,则设置为True
,即如果data ptr
和数组的所有步长都是该对齐的倍数.dtype 构造函数的
align
关键字,仅影响 结构化数组.如果未手动提供结构的字段偏移量,NumPy 会自动确定偏移量.在这种情况下,``align=True`` 会填充结构,使每个字段在内存中”真实”对齐,并将dtype.alignment
设置为字段”真实”对齐的最大值.这类似于 C 结构通常的做法.否则,如果手动提供了偏移量或 itemsize,``align=True`` 仅检查所有字段是否”真实”对齐,并且总 itemsize 是最大字段对齐的倍数.无论哪种情况,:attr:dtype.isalignedstruct
也会设置为 True.IsUintAligned
用于确定一个 ndarray 是否是”uint 对齐”的,类似于IsAligned
检查真实对齐的方式.
对齐的后果#
以下是如何使用上述变量:
创建对齐的结构体:为了知道当
align=True
时如何偏移一个字段,NumPy 查找field.dtype.alignment
.这包括嵌套的结构化数组字段.Ufuncs: 如果数组的
ALIGNED
标志为 False,ufuncs 将在评估前缓冲/转换数组.这是必需的,因为 ufunc 内部循环直接访问原始元素,如果在某些架构上元素不是真正对齐的,这可能会失败.Getitem/setitem/copyswap 函数:类似于 ufuncs,这些函数通常有两条代码路径.如果
ALIGNED
为 False,它们将使用一条缓冲参数的代码路径,使它们真正对齐.跨步复制代码:这里,使用了”uint alignment”.如果数组的itemsize等于1、2、4、8或16字节,并且数组是uint对齐的,那么NumPy将改为执行
*(uintN*)dst) = *(uintN*)src)
以适当的N.否则,NumPy通过执行memcpy(dst, src, N)
进行复制.Nditer 代码:由于这通常会调用跨步复制代码,因此必须检查”uint 对齐”.
转换代码:这检查是否为”真”对齐,如果是,则执行
*dst = CASTFUNC(*src)
.否则,执行memmove(srcval, src); dstval = CASTFUNC(srcval); memmove(dst, dstval)
,其中 dstval/srcval 是对齐的.
请注意,跨步复制和跨步转换代码紧密交织,因此它们处理的任何数组必须是 uint 和 true 对齐的,尽管复制代码只需要 uint 对齐,转换代码只需要 true 对齐.如果这段代码将来被重写,最好允许它们使用不同的对齐方式.