#python #arrays #numpy
#python #массивы #numpy
Вопрос:
Я знаю, что в Python операторы на месте используют __iadd__
метод для операторов на месте. Для неизменяемых типов __iadd__
это обходной путь с использованием __add__
, например, like tmp = a b; a = tmp
, но изменяемые типы (например, списки) изменяются на месте, что приводит к небольшому увеличению скорости.
Однако, если у меня есть массив NumPy, в котором я изменяю содержащиеся в нем неизменяемые типы, например, целые числа или числа с плавающей запятой, это также приводит к еще более значительному увеличению скорости. Как это работает? Я сделал несколько примеров тестов ниже:
import numpy as np
def inplace(a, b):
a = b
return a
def assignment(a, b):
a = a b
return a
int1 = 1
int2 = 1
list1 = [1]
list2 = [1]
npary1 = np.ones((1000,1000))
npary2 = np.ones((1000,1000))
print('Python integers')
%timeit inplace(int1, 1)
%timeit assignment(int2, 1)
print('nPython lists')
%timeit inplace(list1, [1])
%timeit assignment(list2, [1])
print('nNumPy Arrays')
%timeit inplace(npary1, 1)
%timeit assignment(npary2, 1)
Я ожидал бы такой же разницы, что и для целых чисел Python, когда я использовал операторы на месте в массивах NumPy, однако результаты совершенно разные:
Python integers
1000000 loops, best of 3: 265 ns per loop
1000000 loops, best of 3: 249 ns per loop
Python lists
1000000 loops, best of 3: 449 ns per loop
1000000 loops, best of 3: 638 ns per loop
NumPy Arrays
100 loops, best of 3: 3.76 ms per loop
100 loops, best of 3: 6.6 ms per loop
Ответ №1:
Каждый вызов assignment(npary2, 1)
требует создания нового массива из миллиона элементов. Подумайте, сколько времени требуется только для выделения массива единиц в форме (1000, 1000):
In [21]: %timeit np.ones((1000, 1000))
100 loops, best of 3: 3.84 ms per loop
Это выделение нового временного массива требует на моей машине около 3,84 мс и находится на правильном порядке величины, чтобы объяснить всю разницу между inplace(npary1, 1)
и assignment(nparay2, 1)
:
In [12]: %timeit inplace(npary1, 1)
1000 loops, best of 3: 1.8 ms per loop
In [13]: %timeit assignment(npary2, 1)
100 loops, best of 3: 4.04 ms per loop
Итак, учитывая, что распределение является относительно медленным процессом, имеет смысл, что добавление на месте происходит значительно быстрее, чем присвоение новому массиву.
Операции NumPy над массивами NumPy могут быть быстрыми, но создание массивов NumPy происходит относительно медленно. Рассмотрим, например, насколько больше времени требуется для создания массива NumPy, чем списка Python:
In [14]: %timeit list()
10000000 loops, best of 3: 106 ns per loop
In [15]: %timeit np.array([])
1000000 loops, best of 3: 563 ns per loop
Это одна из причин, по которой обычно лучше использовать один большой массив NumPy (выделенный один раз), а не тысячи маленьких массивов NumPy.