#python #performance #numpy
#python #Производительность #numpy
Вопрос:
я пытаюсь ускорить следующий код:
import time
import numpy as np
np.random.seed(10)
b=np.random.rand(10000,1000)
def f(a=1):
tott=0
for _ in range(a):
q=np.array(b)
t1 = time.time()
for i in range(len(q)):
for j in range(len(q[0])):
if q[i][j]>0.5:
q[i][j]=1
else:
q[i][j]=-1
t2=time.time()
tott =t2-t1
print(tott/a)
Как вы можете видеть, в основном функция заключается в повторении в двойном цикле. Итак, я попытался использовать np.nditer
, np.vectorize
и map
вместо этого. If дает некоторое ускорение (например, в 4-5 раз, за исключением np.nditer
), но! с np.where(q>0.5,1,-1)
ускорением почти в 100 раз.
Как я могу выполнять итерации по массивам numpy так быстро, как np.where
это делает? И почему это намного быстрее?
Ответ №1:
Это потому, что ядро numpy реализовано на C. Вы в основном сравниваете скорость C с Python.
Если вы хотите использовать преимущество numpy в скорости, вам следует выполнять как можно меньше вызовов в вашем коде Python. Если вы используете цикл Python, вы уже проиграли, даже если вы используете функции numpy только в этом цикле. Используйте функции более высокого уровня, предоставляемые numpy (вот почему они поставляют так много специальных функций). Внутренне он будет использовать гораздо более эффективный цикл (C-)
Вы можете самостоятельно реализовать функцию на C (с циклами) и вызвать ее из Python. Это должно дать сопоставимые скорости.
Комментарии:
1. Если вы используете стандартный интерпретатор python (CPython), сам python написан на C. Дело в том, что
numpy
использует C / Fortran (?) код, который сам по себе оптимизирован. Ключевым моментом здесь является использование векторных операций2. @user8408080 конечно, в конце концов, все сводится к машинному коду. Однако CPython интерпретируется во время компиляции C (или Fortran). Известно, что Python не является самым быстрым интерпретируемым языком (и он никогда не был разработан таким образом, iirc).
3. Я согласен, что это, возможно, придирка, но на первый взгляд это звучало немного странно. 1 тыс.
Ответ №2:
Чтобы ответить на этот вопрос, вы можете получить ту же скорость (100-кратное ускорение), используя numba
библиотеку:
from numba import njit
def f(b):
q = np.zeros_like(b)
for i in range(b.shape[0]):
for j in range(b.shape[1]):
if q[i][j] > 0.5:
q[i][j] = 1
else:
q[i][j] = -1
return q
@njit
def f_jit(b):
q = np.zeros_like(b)
for i in range(b.shape[0]):
for j in range(b.shape[1]):
if q[i][j] > 0.5:
q[i][j] = 1
else:
q[i][j] = -1
return q
Сравните скорость:
Обычный Python
%timeit f(b)
592 ms ± 5.72 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Numba (точно в срок скомпилирован с использованием скорости LLVM ~ C)
%timeit f_jit(b)
5.97 ms ± 105 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)