Великолепный разрыв в производительности между len(arr) и arr.shape[0]
Я нашел это len(arr)
почти в два раза быстрее, чем arr.shape[0]
и мне интересно, почему.
Я использую Python 3.5.2, Numpy 1.14.2, IPython 6.3.1
Код ниже демонстрирует это:
arr = np.random.randint(1, 11, size=(3, 4, 5))
%timeit len(arr)
# 62.6 ns ± 0.239 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit arr.shape[0]
# 102 ns ± 0.163 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Я также сделал еще несколько тестов для сравнения:
class Foo():
def __init__(self):
self.shape = (3, 4, 5)
foo = Foo()
%timeit arr.shape
# 75.6 ns ± 0.107 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit foo.shape
# 61.2 ns ± 0.281 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit foo.shape[0]
# 78.6 ns ± 1.03 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Итак, у меня есть два вопроса:
1) Почему len(arr)
работает быстрее чем arr.shape[0]
? (Я бы подумал len
будет медленнее из-за вызова функции)
2) Почему foo.shape[0]
работать быстрее чем arr.shape[0]
? (Другими словами, какие накладные расходы возникают в этом случае при работе с пустыми массивами?)
1 ответ
Структура данных массива numpy реализована на C. Размеры массива хранятся в структуре C. Они не хранятся в кортеже Python. Поэтому каждый раз, когда вы читаете shape
атрибут, создается новый кортеж Python новых целочисленных объектов Python. Когда вы используете arr.shape[0]
Затем этот кортеж индексируется, чтобы вытащить первый элемент, что добавляет немного дополнительной информации. len(arr)
нужно только создать целое число Python.
Вы можете легко убедиться, что arr.shape
создает новый кортеж каждый раз, когда он читается:
In [126]: arr = np.random.randint(1, 11, size=(3, 4, 5))
In [127]: s1 = arr.shape
In [128]: id(s1)
Out[128]: 4916019848
In [129]: s2 = arr.shape
In [130]: id(s2)
Out[130]: 4909905024
s1
а также s2
есть разные id
s; это разные объекты кортежей.