Выравнивание памяти#
Цели выравнивания NumPy#
Существует три случая использования, связанных с выравниванием памяти в NumPy (по состоянию на 1.14):
Создание структурированные типы данных с поля выровнены как в C-структуре.
Ускорение операций копирования с использованием
uintприсваивание вместоmemcpy.Гарантирование безопасного выровненного доступа для ufuncs/setitem/casting кода.
NumPy использует две различные формы выравнивания для достижения этих целей: «Истинное выравнивание» и «Выравнивание Uint».
"Истинное" выравнивание относится к архитектурно-зависимому выравниванию эквивалентного C-типа в C. Например, в системах x64 float64 эквивалентно double в C. В большинстве систем это имеет выравнивание 4 или 8 байт (и это можно контролировать в GCC с помощью опции
malign-double). Переменная выровнена в памяти, если её смещение в памяти кратно её выравниванию. На некоторых системах (например, sparc) выравнивание памяти обязательно; на других оно даёт ускорение.
Выравнивание "Uint" зависит от размера типа данных. Оно определяется как "истинное выравнивание" uint, используемого кодом копирования NumPy для копирования типа данных, или неопределённое/невыровненное, если нет эквивалентного 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):
архитектура
тип
true-aln
uint-aln
x86_64
complex64
4
8
x86_64
float128
16
8
x86
float96
4
-
Переменные в NumPy, которые контролируют и описывают выравнивание#
Существует 4 соответствующих использования слова align используется в NumPy:
The
dtype.alignmentатрибут (descr->alignmentв C). Это предназначено для отражения «истинного выравнивания» типа. Имеет зависящие от архитектуры значения по умолчанию для всех типов данных, кроме структурированных типов, созданных с помощьюalign=Trueкак описано ниже.The
ALIGNEDфлаг ndarray, вычисленный вIsAlignedи проверяется с помощьюPyArray_ISALIGNED. Это вычисляется изdtype.alignment. Установлено вTrueесли каждый элемент в массиве находится в ячейке памяти, соответствующейdtype.alignment, что имеет место, еслиdata ptrи все шаги массива кратны этому выравниванию.The
alignключевое слово конструктора dtype, которое влияет только на Структурированные массивы. Если смещения полей структуры не указаны вручную, NumPy определяет смещения автоматически. В этом случаеalign=Trueдополняет структуру так, чтобы каждое поле было «истинно» выровнено в памяти и устанавливаетdtype.alignmentбыть наибольшим из "истинных" выравниваний полей. Это похоже на то, что обычно делают C-структуры. В противном случае, если смещения или размер элемента были предоставлены вручнуюalign=Trueпросто проверяет, что все поля выровнены «истинно» и что общий размер элементов кратен наибольшему выравниванию поля. В любом случаеdtype.isalignedstructтакже установлено в True.IsUintAlignedиспользуется для определения, является ли ndarray "uint выровненным", аналогично тому, какIsAlignedпроверяет на истинное выравнивание.
Последствия выравнивания#
Вот как используются переменные выше:
Создание выровненных структур: Чтобы узнать, как сместить поле, когда
align=True, NumPy ищетfield.dtype.alignment. Это включает поля, которые являются вложенными структурированными массивами.Универсальные функции: Если
ALIGNEDфлаг массива равен False, ufuncs будут буферизовать/приводить массив перед вычислением. Это необходимо, поскольку внутренние циклы ufunc обращаются к сырым элементам напрямую, что может не сработать на некоторых архитектурах, если элементы не выровнены по истине.Функция getitem/setitem/copyswap: аналогично ufuncs, эти функции обычно имеют два пути выполнения. Если
ALIGNEDравно False, они будут использовать путь кода, который буферизует аргументы, чтобы они были истинно выровнены.Код копирования с шагом: здесь вместо этого используется «uint alignment». Если размер элемента массива равен 1, 2, 4, 8 или 16 байтам и массив выровнен по uint, то NumPy выполнит
*(uintN*)dst) = *(uintN*)src)для соответствующего N. В противном случае NumPy копирует, выполняяmemcpy(dst, src, N).Код Nditer: Поскольку это часто вызывает код копирования с шагом, он должен проверять "выравнивание uint".
Код приведения: Это проверяет «истинное» выравнивание, как это делает
*dst = CASTFUNC(*src)если выровнено. В противном случае, не делаетmemmove(srcval, src); dstval = CASTFUNC(srcval); memmove(dst, dstval)где dstval/srcval выровнены.
Обратите внимание, что код поэлементного копирования и поэлементного приведения тесно переплетены, поэтому любые массивы, обрабатываемые ими, должны быть одновременно выровнены по uint и истинно выровнены, даже хотя код копирования требует только выравнивания по uint, а код приведения — только истинного выравнивания. Если когда-нибудь будет большая переработка этого кода, было бы хорошо позволить им использовать разные выравнивания.