Скорость добавления массивов numpy с разным непрерывным типом

#python #arrays #performance #numpy #contiguous

#питон #массивы #Производительность #numpy #смежный #python

Вопрос:

Массивы Numpy хранятся с разными непрерывными типами (C- и F-). При использовании numpy.swapaxes() непрерывный тип изменяется. Мне нужно добавить два многомерных массива (точнее, 3d), один из которых поступает из другого массива с замененными осями. Я заметил, что когда первая ось заменяется последней осью, в случае 3d-массива непрерывный тип изменяется с C- на F-. И добавление двух массивов с разным непрерывным типом происходит чрезвычайно медленно (~ в 6 раз медленнее, чем добавление двух смежных с C массивов). Однако, если поменять местами другие оси (0-1 или 1-2), результирующий массив будет иметь ложные флаги как для C-, так и для F- смежных (несмежных). Для меня странно то, что добавление одного массива C-configuous и одного массива, не являющегося ни C-, ни F-смежным, на самом деле лишь немного медленнее, чем добавление двух массивов одного и того же типа. Вот мои два вопроса:

  1. Почему кажется, что это отличается для добавления C- и F-смежных arrray и добавления C- и несмежных массивов? Это вызвано другим механизмом перестановки или просто потому, что расстояние перестановки между C- и F-смежными является самым длинным для всех возможных порядков осей?

  2. Если мне нужно добавить C-непрерывный массив и F-непрерывный / несмежный массив, каков наилучший способ ускорить скорость?

Ниже приведен минимальный пример того, с чем я столкнулся. Три длительности печати на моем компьютере составляют 2,0 с (C-непрерывный C-непрерывный), 12,4 с (C-непрерывный F-непрерывный), 3,4 с (C-непрерывный несмежный) и 3,3 с (C-непрерывный несмежный).

 import numpy as np
import time

np.random.seed(1234)

a = np.random.random((300, 400, 500))  # C-contiguous
b = np.swapaxes(np.random.random((500, 400, 300)), 0, 2)  # F-contiguous
c = np.swapaxes(np.random.random((300, 500, 400)), 1, 2)  # Non-contiguous
d = np.swapaxes(np.random.random((400, 300, 500)), 0, 1)  # Non-contiguous

t = time.time()
for n in range(10):
    result = a   a
print(time.time() - t)

t = time.time()
for n in range(10):
    result = a   b
print(time.time() - t)

t = time.time()
for n in range(10):
    result = a   c
print(time.time() - t)

t = time.time()
for n in range(10):
    result = a   d
print(time.time() - t)
  

Ответ №1:

Эти типы ( F и C ) обозначают, хранится ли матрица (или многомерный массив) в основном столбце ( C как в языке C, использующем хранилище с большим количеством столбцов) или в основном ряду ( F как в языке Fortran, использующем хранилище с большим количеством строк).

Оба на самом деле не различаются по скорости. Это просто уровень абстракции. Независимо от того, какой из них вы используете, он обеспечивает одинаковую производительность.

Однако огромная разница заключается в том, являются ли массивы непрерывными или нет. Если они являются смежными, у вас будут хорошие тайминги из-за эффектов кэширования, векторизации и других игр оптимизации, которые может применить компилятор.

Комментарии:

1. Спасибо за быстрый ответ, Армен! Я понимаю, что непрерывный тип является уровнем абстракции. Но при добавлении двух массивов с разными смежными типами в python кажется, что выполняется определенный тип преобразования, что приводит к очень плохой производительности. Я обновил пример кода, который демонстрирует это поведение, по крайней мере, на моем компьютере.

2. Действительно, преобразование начинается там.