内存对齐#
NumPy 对齐目标#
NumPy 中存在与内存对齐相关的三个用例(截至 1.14):
NumPy 使用两种不同形式的对齐来实现这些目标:“真实对齐”和“Uint 对齐”。
“真正的”对齐是指 C 中等效 C 类型的依赖于体系结构的对齐。例如,在 x64 系统中float64
相当于double
C 中的对齐。在大多数系统上,它具有 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)系统上,NumPycomplex64
数据类型实现为.它的“真实”对齐为 4,“uint”对齐为 8(等于 的真实对齐
)。struct { float real, imag; }
uint64
- uint 和 true 对齐方式不同的某些情况(默认 GCC Linux):
拱
类型
真阿尔恩
uint-aln
x86_64
复杂64
4
8
x86_64
浮动128
16
8
x86
浮动96
4
-
NumPy 中控制和描述对齐的变量#
align
NumPy 中使用的该词有 4 种相关用法:
属性
dtype.alignment
(descr->alignment
在 C 中)。这是为了反映类型的“真实对齐”。它对所有数据类型都具有依赖于体系结构的默认值,但使用align=True
如下所述创建的结构化类型除外。
ALIGNED
ndarray 的标志,由 计算和IsAligned
检查PyArray_ISALIGNED
。这是从 计算出来的dtype.alignment
。True
如果数组中的每个项目都位于与 一致的内存位置,则将其设置为 ,如果数组的 和所有步幅都是该对齐方式的倍数,dtype.alignment
就是这种情况 。data ptr
dtype 构造函数的关键字
align
,仅影响 结构化数组。如果未手动提供结构体的字段偏移量,NumPy 会自动确定偏移量。在这种情况下,align=True
填充结构,以便每个字段在内存中“真”对齐,并设置dtype.alignment
为字段“真”对齐中最大的一个。这就像 C 结构通常所做的那样。否则,如果手动提供偏移量或项目大小,align=True
则只需检查所有字段是否“真实”对齐,并且总项目大小是否是最大字段对齐的倍数。在任一情况下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 对齐”。如果数组的项大小等于 1、2、4、8 或 16 字节,并且该数组是 uint 对齐的,则 NumPy 将执行适当的 N。否则,NumPy 将通过执行 进行复制。
*(uintN*)dst) = *(uintN*)src)
memcpy(dst, src, N)
Nditer 代码:由于这通常调用跨步复制代码,因此它必须检查“uint 对齐”。
转换代码:这会检查“真实”对齐,就像 对齐时一样。否则,它会 在 dstval/srcval 对齐的地方执行。
*dst = CASTFUNC(*src)
memmove(srcval, src); dstval = CASTFUNC(srcval); memmove(dst, dstval)
请注意,strided-copy 和 strided-cast 代码深深地交织在一起,因此它们处理的任何数组都必须同时进行 uint 和 true 对齐,即使复制代码只需要 uint 对齐,而强制转换代码只需要 true 对齐。如果对这段代码进行大规模重写,最好允许他们使用不同的对齐方式。