Выравнивание памяти
Цели выравнивания памяти в Numpy
Существует три варианта использования, связанных с выравниванием памяти в Numpy (начиная с версии 1.14):
- Создание структурированных типов данных с выравниванием полей, как в C-структуре.
- Ускорение операций копирования, используя присваивание uint вместо memcpy
- Обеспечение безопасного выровненного доступа для функций ufuncs/setitem/преобразования кода
Numpy использует две различные формы выравнивания для достижения этих целей: «истинное выравнивание» и «выравнивание по uint».
«Истинное» выравнивание относится к архитектурно-зависимому выравниванию эквивалентного C-типа в C. Например, в системах x64 numpy.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):
-
тип архитектуры истинное-выравнивание uint-выравнивание —- —- ——– ——– x86_64 complex64 4 8 x86_64 float128 16 8 x86 float96 4 -
Переменные в Numpy, которые контролируют и описывают выравнивание
В Numpy используются 4 соответствующих случая использования слова align.
- Атрибут
dtype.alignment(descr->alignmentв C). Он предназначен для отражения «истинного выравнивания» типа. Он имеет значения по умолчанию, зависящие от архитектуры, для всех типов данных, за исключением структурированных типов, созданных с помощьюalign=True, как описано ниже. - Флаг
ALIGNEDndarray, вычисленный вIsAlignedи проверенныйPyArray_ISALIGNED. Он вычисляется изdtype.alignment. Он устанавливается в значениеTrue, если каждый элемент в массиве находится в позиции памяти, соответствующейdtype.alignment, что имеет место, если указатель данных и все шаги массива кратны этому выравниванию. - Ключевое слово
alignконструктора dtype, которое влияет только на структурированные массивы. Если смещения полей структуры не заданы вручную, 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 выполнит
*(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, а код преобразования — только в истинном выравнивании. Если когда-либо будет произведена большая переработка этого кода, было бы полезно разрешить им использовать различные выравнивания.
© 2005–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.19/reference/alignment.html