内存对齐

numpy对齐目标

numpy中有三个与内存对齐相关的用例(从1.14开始):

  1. 创建与C结构中的字段一样对齐的结构化数据类型。

  2. 使用uint赋值代替memcpy加速拷贝操作

  3. 确保UFUNCS/SETITEM/Casting代码的安全对齐访问

numpy使用两种不同的对齐方式来实现这些目标:“真正对齐”和“uint对齐”。

“真”对齐是指C中等效C类型的依赖于体系结构的对齐。例如,在X64系统中 numpy.float64 等于 double 在C.中,在大多数系统中,这要么是4字节,要么是8字节(这可以在GCC中由选项控制 malign-double )如果变量的内存偏移量是其对齐的倍数,则该变量在内存中对齐。在某些系统(如SPARC)上,内存对齐是必需的,而在其他系统上,则会加速。

“uint”对齐取决于数据类型的大小。它被定义为numpy的复制代码用来复制数据类型的uint的“真正对齐”,如果没有等价的uint,则定义为undefined/unalinted。目前,numpy使用uint8、uint16、uint32、uint64和uint64分别复制大小为1、2、4、8、16字节的数据,并且所有其他大小的数据类型不能uint对齐。

例如,在(典型的Linux x64 gcc)系统上, complex64 数据类型实现为 struct {{ float real, imag; }} . 它的“真”对齐为4,“uint”对齐为8(等于 uint64

在某些情况下,uint和true对齐是不同的(默认gcc-linux):

arch type true aln uint aln-----------------------x86_64 complex64 4 8 x86_64 float128 16 8 x86 float96 4-

numpy中控制和描述对齐的变量

这个词有4个相关用法 align 用于 numpy:

  • 这个 dtype.alignment 属性 (descr->alignment 在C)中。这是为了反映类型的“真正对齐”。除了使用 align=True 如下所述。

  • 这个 ALIGNED 数据数组的标志,计算位置 IsAligned 并通过检查 PyArray_ISALIGNED . 这是根据 dtype.alignment . 它被设定为 True 如果数组中的每个项都位于与 dtype.alignment 如果数据指针和数组的所有步数都是该对齐方式的倍数,则为这种情况。

  • 这个 align dtype构造函数的关键字,它只影响结构化数组。如果未手动提供结构的字段偏移,numpy将自动确定偏移。在那种情况下, align=True 填充结构,使每个字段在内存和集合中“真”对齐 dtype.alignment 要成为字段“真”对齐中最大的一个。这就像C-structs通常做的那样。否则,如果手动提供偏移量或项大小 align=True 只需检查所有字段是否“真”对齐,以及总项大小是否是最大字段对齐的倍数。在任何一种情况下 dtype.isalignedstruct 也设置为“真”。

  • IsUintAligned 用于确定ndarray是否以类似于 IsAligned 检查是否正确对齐。

对齐的结果

以下是如何使用上述变量:

  1. 创建对齐结构:以便知道如何在 align=True ,Numpy 查找 field.dtype.alignment . 这包括嵌套结构数组的字段。

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

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

  4. 跨步复制代码:这里使用“uint-alignment”代替。如果数组的itemsize等于1、2、4、8或16字节,并且数组是uint对齐的,那么numpy将执行此操作。 *(uintN*)dst) = *(uintN*)src) 对于适当的N。否则通过 NumPy memcpy(dst, src, N) 做副本.

  5. nditer代码:因为这经常调用跨步复制代码,所以必须检查“uint-alignment”。

  6. CAST代码:这检查“真”对齐,就像它检查一样 *dst = CASTFUNC(*src) 如果对齐。否则,就是这样 memmove(srcval, src); dstval = CASTFUNC(srcval); memmove(dst, dstval) 其中dstval/srcval对齐。

请注意,跨步复制和跨步强制转换代码是紧密交织在一起的,因此它们处理的任何数组都必须同时进行uint和true对齐,即使复制代码只需要uint对齐,而强制转换代码只需要true对齐。如果这段代码有过大幅度的重写,那么最好允许它们使用不同的对齐方式。