記憶體對齊#
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 的「真」對齊和 8 的「uint」對齊(等於 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
和所有 strides 都是該對齊方式的倍數,則會是這種情況。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 對齊」。如果陣列的項目大小等於 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 對齊,而轉換程式碼僅需要真對齊。如果將來要大幅重寫此程式碼,最好允許它們使用不同的對齊方式。