Пчему numpy.ndarray медленнее, чем list?

from math import sqrt
import numpy as np
import ctypes
import time

w, h = ctypes.windll.user32.GetSystemMetrics(0),\
       ctypes.windll.user32.GetSystemMetrics(1)

arr = np.ndarray((w, h), 'f')

t = time.time()
for ix in range(w):
    for iy in range(h):
        arr[ix, iy] = sqrt((w/2 - ix)**2+(h/2-iy)**2)
print(time.time()-t)

arr =[[0 for _ in range(w)] for _ in range(h)]

t = time.time()
for ix in range(w):
    for iy in range(h):
        arr[iy][ix] = sqrt((w/2 - ix)**2+(h/2-iy)**2)
print(time.time()-t)

Ответы (1 шт):

Автор решения: CrazyElf

Я попробовал ваш код у себя локально под Windows, а также в Google Colab. И вот что я могу сказать:

  • Numpy не медленнее, чем ванильный Python, но и не сильно быстрее, если использовать от Numpy только структуру данных, а весь подход к обработке и все функции при этом брать обычные питоновские, ускорения при этом никакого не будет, конечно
  • Ваш код ещё и даёт несколько разные результаты при работе с Numpy и в чистом Python, потому что в первом случае работа идёт с float32, а во втором с float64 и округление при этом получается довольно заметно разным, если просуммировать элементы массивов
  • Чтобы Numpy работал заметно быстрее, нужно использовать векторизацию, т.е. работать не с одиночными элементами массива по отдельности, а с целыми строками или столбцами, а лучше сразу с целой матрицей.

Вот я навскидку сделал код с Numpy заметно более быстрым (примерно в 40 раз) и дающим более одинаковый результат в обоих вариантах. Ускорение достигнуто за счёт удаления одного из циклов и использования вместо него векторного вычисления сразу ряда значений с использованием соответствующих функций Numpy:

from math import sqrt
import numpy as np
import ctypes
import time

try:
    w, h = ctypes.windll.user32.GetSystemMetrics(0),\
           ctypes.windll.user32.GetSystemMetrics(1)
except AttributeError:
    w, h = 1536, 864

print(w, h)
arr = np.ndarray((w, h), np.float64)
print(arr.dtype)

t = time.time()
for ix in range(w):
    arr[ix, :] = np.sqrt((w/2 - ix)**2+(h/2-np.arange(h))**2)
print(time.time()-t)
print(arr.sum())

arr =[[0 for _ in range(w)] for _ in range(h)]

t = time.time()
for ix in range(w):
    for iy in range(h):
        arr[iy][ix] = sqrt((w/2 - ix)**2+(h/2-iy)**2)
print(time.time()-t)
print(sum(arr[j][i] for i in range(w) for j in range(h)))

Результат в Google Colab:

1536 864
float64
0.01708245277404785
623787523.1485264
0.7624058723449707
623787523.1485249

Результат под Windows в Jupyter Notebook:

1536 864
float64
0.0567629337310791
623787523.1485264
1.9754235744476318
623787523.1485249
→ Ссылка