内存对齐#
NumPy 对齐目标#
在 NumPy 中,与内存对齐相关的用例有三个(截至 1.14 版本)
NumPy 使用两种不同的对齐方式来实现这些目标:“真对齐”和“Uint 对齐”。
“真”对齐是指C语言中等效C类型的架构相关对齐。例如,在x64系统中,float64
等效于C语言中的double
。在大多数系统上,它的对齐方式为4或8字节(这可以通过GCC选项malign-double
控制)。如果变量的内存偏移量是其对齐方式的倍数,则该变量在内存中对齐。在某些系统(例如sparc)上,需要内存对齐;在其他系统上,它可以提高速度。
“Uint”对齐取决于数据类型的尺寸。它被定义为NumPy复制代码用来复制数据类型的uint的“真对齐”,如果没有等效的uint,则未定义/未对齐。目前,NumPy使用uint8
,uint16
,uint32
,uint64
和uint64
分别复制大小为1、2、4、8、16字节的数据,所有其他大小的数据类型都不能进行uint对齐。
例如,在一个(典型的Linux x64 GCC)系统上,NumPy complex64
数据类型实现为struct { float real, imag; }
。“真”对齐为4,“uint”对齐为8(等于uint64
的真对齐)。
- 一些uint和真对齐不同的情况(默认GCC Linux)
架构
类型
真对齐
uint对齐
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是否是最大字段对齐的倍数。在这两种情况下,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对齐”。如果数组的itemsize等于1、2、4、8或16字节,并且数组是uint对齐的,则NumPy将对合适的N执行
*(uintN*)dst) = *(uintN*)src)
。否则,NumPy通过执行memcpy(dst, src, N)
来复制。Nditer代码:由于这通常调用步幅复制代码,因此必须检查“uint对齐”。
转换代码:这会检查“真”对齐,因为它如果对齐则执行
*dst = CASTFUNC(*src)
。否则,它执行memmove(srcval, src); dstval = CASTFUNC(srcval); memmove(dst, dstval)
,其中dstval/srcval是对齐的。
请注意,步幅复制和步幅转换代码是紧密相关的,因此任何由它们处理的数组都必须同时进行uint和真对齐,即使复制代码只需要uint对齐,转换代码只需要真对齐。如果此代码将来进行大规模重写,最好允许它们使用不同的对齐方式。