内存对齐#

NumPy 对齐目标#

NumPy 中存在与内存对齐相关的三个用例(截至 1.14):

  1. 创建结构化数据类型,其中 字段对齐,就像 C 结构中一样。

  2. uint通过使用in 代替 中的赋值 来加速复制操作memcpy

  3. 确保 ufuncs/setitem/cast 代码的安全对齐访问。

NumPy 使用两种不同形式的对齐来实现这些目标:“真实对齐”和“Uint 对齐”。

“真正的”对齐是指 C 中等效 C 类型的依赖于体系结构的对齐。例如,在 x64 系统中float64相当于doubleC 中的对齐。在大多数系统上,它具有 4 或 8 字节的对齐(并且这可以在 GCC 中由选项控制 malign-double)。如果变量的内存偏移量是其对齐方式的倍数,则该变量在内存中对齐。在某些系统(例如 sparc)上需要内存对齐;对其他人来说,它会加速。

“Uint”对齐取决于数据类型的大小。它被定义为 NumPy 的复制代码用于复制数据类型的 uint 的“真实对齐”,或者如果没有等效的 uint,则为未定义/未对齐。目前,NumPy 使用 uint8uint16uint32uint64、 和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 中控制和描述对齐的变量#

alignNumPy 中使用的该词有 4 种相关用法:

  • 属性dtype.alignmentdescr->alignment在 C 中)。这是为了反映类型的“真实对齐”。它对所有数据类型都具有依赖于体系结构的默认值,但使用align=True如下所述创建的结构化类型除外。

  • ALIGNEDndarray 的标志,由 计算和IsAligned检查PyArray_ISALIGNED。这是从 计算出来的 dtype.alignmentTrue如果数组中的每个项目都位于与 一致的内存位置,则将其设置为 ,如果数组的 和所有步幅都是该对齐方式的倍数,dtype.alignment就是这种情况 。data ptr

  • dtype 构造函数的关键字align,仅影响 结构化数组。如果未手动提供结构体的字段偏移量,NumPy 会自动确定偏移量。在这种情况下, align=True填充结构,以便每个字段在内存中“真”对齐,并设置dtype.alignment为字段“真”对齐中最大的一个。这就像 C 结构通常所做的那样。否则,如果手动提供偏移量或项目大小,align=True则只需检查所有字段是否“真实”对齐,并且总项目大小是否是最大字段对齐的倍数。在任一情况下dtype.isalignedstruct 也设置为 True。

  • IsUintAligned用于确定 ndarray 是否“uint 对齐”,其方式与IsAligned检查真实对齐方式类似。

对齐的后果#

以下是上面变量的使用方式:

  1. 创建对齐结构:要了解如何在 时偏移字段 align=True,NumPy 会查找field.dtype.alignment。这包括嵌套结构化数组的字段。

  2. Ufuncs:如果ALIGNED数组的标志为 False,ufuncs 将在计算之前缓冲/转换数组。这是必需的,因为 ufunc 内部循环直接访问原始元素,如果元素未真正对齐,则在某些拱门上可能会失败。

  3. getitem/setitem/copyswap函数:与ufuncs类似,这些函数一般有两个代码路径。如果ALIGNED为 False,它们将使用缓冲参数的代码路径,以便它们真正对齐。

  4. 跨步复制代码:这里使用“uint 对齐”。如果数组的项大小等于 1、2、4、8 或 16 字节,并且该数组是 uint 对齐的,则 NumPy 将执行适当的 N。否则,NumPy 将通过执行 进行复制。*(uintN*)dst) = *(uintN*)src)memcpy(dst, src, N)

  5. Nditer 代码:由于这通常调用跨步复制代码,因此它必须检查“uint 对齐”。

  6. 转换代码:这会检查“真实”对齐,就像 对齐时一样。否则,它会 在 dstval/srcval 对齐的地方执行。*dst = CASTFUNC(*src)memmove(srcval, src); dstval = CASTFUNC(srcval); memmove(dst, dstval)

请注意,strided-copy 和 strided-cast 代码深深地交织在一起,因此它们处理的任何数组都必须同时进行 uint 和 true 对齐,即使复制代码只需要 uint 对齐,而强制转换代码只需要 true 对齐。如果对这段代码进行大规模重写,最好允许他们使用不同的对齐方式。