Нашли ошибку?
продвинутыйNumPy 2.5.3

Порядок памяти и strides

Как массив хранится в памяти, почему транспонирование ничего не копирует и когда порядок влияет на скорость.

Нужно знать:Представления и копииОси: T, newaxis, squeeze

Содержание статьи

Коротко

  • Данные массива лежат в памяти одним блоком. Форма и strides говорят, как по нему ходить: на сколько байт сдвинуться, чтобы перейти к следующему элементу по каждой оси.
  • C-порядок (по умолчанию) — строка за строкой; F-порядок (Fortran) — столбец за столбцом.
  • Транспонирование, срезы с шагом, broadcast_to меняют только strides — поэтому они бесплатны и дают представления.
  • flags["C_CONTIGUOUS"] показывает, лежат ли элементы подряд в порядке C. Некоторые операции (ravel, reshape, передача в C-библиотеки) для несплошных массивов копируют данные.
  • Проход вдоль последней оси C-массива быстрее, чем вдоль первой: элементы идут подряд.

Синтаксис

a.strides                    # (байт по оси 0, байт по оси 1, …)
a.flags["C_CONTIGUOUS"], a.flags["F_CONTIGUOUS"]
np.ascontiguousarray(a)      # копия в порядке C, если нужно
np.asfortranarray(a)         # копия в порядке F, если нужно
np.zeros((3, 4), order="F")

Примеры

Данные для примеровиспользуются во всех примерах ниже
m = np.arange(12, dtype=np.int64).reshape(3, 4)
strides и транспонирование
print(m.strides)          # байт до следующей строки и до следующего столбца
m.T.strides               # транспонирование только меняет strides местами
Вывод
(32, 8)
(8, 32)
Сплошной ли массив
print(m.flags["C_CONTIGUOUS"], m.flags["F_CONTIGUOUS"])
m.T.flags["C_CONTIGUOUS"], m.T.flags["F_CONTIGUOUS"]
Вывод
True False
(False, True)
Порядок F
f = np.asfortranarray(m)  # те же значения, в памяти по столбцам
print(f.strides)
np.array_equal(f, m)
Вывод
(8, 24)
True
Срез с шагом
every_other = m[:, ::2]
print(every_other.strides)                 # шаг по столбцам вдвое больше
every_other.flags["C_CONTIGUOUS"]          # данные идут с пропусками
Вывод
(32, 16)
False
Сделать сплошным
c = np.ascontiguousarray(m.T)              # копия в порядке C
c.flags["C_CONTIGUOUS"], np.shares_memory(c, m)
Вывод
(True, False)

Подводные камни

ravel и reshape иногда копируют

Для несплошного массива (например, транспонированного) вытянуть элементы в строку без копии нельзя, и ravel молча копирует. Если важно, чтобы копии не было, используйте a.reshape(-1, copy=False) — он упадёт вместо копирования.

ravel для транспонированного массива
print(np.shares_memory(m.ravel(), m))      # C-массив вытягивается без копии
np.shares_memory(m.T.ravel(), m)           # транспонированный — только с копией
Вывод
True
False