Извините, но я не думаю, что ответ @millimoose хорошо объясняет, что на самом деле происходит или что имел в виду автор, когда сказал, что массив быстрее списка.
Объем памяти:
Для дублирования требуется 8 байтов, и это именно то количество памяти, которое необходимо, если вы храните его в array
- он хранится не как Python-Float, а каксырое 8-байтовое значение.Однако из-за перераспределения и некоторых дополнительных данных, сохраняемых в объекте массива, возникают небольшие накладные расходы (размер массива, размер буфера, тип значений в массиве и т. Д.).
A Python-Float требуется более 8 байтов:
>>> import sys
>>> f=1.0
>>> sys.getsizeof(f)
24
24 байта - довольно мало для Python-объекта!Например, обычному пустому объекту Python потребуется (Python3):
>>> class A:
pass
>>> a=A()
>>> sys.getsizeof(a)
56
56 байт.Есть приемы, позволяющие уменьшить количество необходимых байтов, и все они используются для Python-Float, но вам все равно нужно 8 байтов для двойного значения, еще 8 байтов для счетчика ссылок и 8 байтов для указателя на описание типа.(таким образом, объект знает, что это объект Float).
Кроме того, в списке хранится не сам объект, а ссылка на него (т. е. указатель), для которого требуется 8 байтов.Таким образом, для сохранения Python-плавающего числа в списке необходимо 32 байта, что в 4 раза превышает объем используемой памяти.
Так почему же вы видите что-то другое при вызове sys.getsizeof
для списка?Ответ: sys.getsizeof
является не рекурсивным :
sys.getsizeof (object [, default])
....
Учитывается только потребление памяти , непосредственно приписываемое объекту, а не потребление памяти объектами, к которым он относится.
Это означает, что getsizeof
для списка имеет значение толькопамять, необходимая для ссылок на объекты Float (8 байт на ссылку), а не размер объектов.Чтобы проиллюстрировать это:
>>> lst=[list(range(1000))]
>>> sys.getsizeof(lst)
72
Очевидно, что используется больше памяти, чем указано в 72 байтах.
Чтобы увидеть реальное потребление памяти, вам необходимо учитывать объем памяти, необходимый интерпретатору:
>>> /usr/bin/time -fpeak_used_memory:%M python -c "l=list((float(i) for i in range(10**7)))"
peak_used_memory:326832
>>> /usr/bin/time -fpeak_used_memory:%M python -c "import array; a=array.array('d',(float(i) for i in range(10**7)))"
peak_used_memory:88076
Как мы видим, разница (320 МБ против 80 МБ) примерно равна ожидаемому коэффициенту 4.
Скорость:
Автор не являетсясказав, что использование array.array
с python-интерпретатором даст вам ускорение.Напротив, использование array.array
с python-операциями замедлит работу, потому что сначала необработанные двойные значения должны быть преобразованы в Python-Floats:
lst=list(range(10**6))
%timeit sum(lst)
7.19 ms ± 461 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
import array
a=array.array('i',range(10**6))
%timeit sum(a)
17.9 ms ± 43.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
почти в 3 раза медленнее!
Тем не менее, есть потенциал, чтобы ускорить процесс - это не так просто.Для этого можно использовать NumPy, Cython или Numba.Например:
import numpy as np
b=np.array(range(10**6), dtype=np.int32)
%timeit b.sum()
1.07 ms ± 24.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Почти в 10 раз быстрее!