Почему numpy.array так медленно?
Я озадачен этим
def main():
for i in xrange(2560000):
a = [0.0, 0.0, 0.0]
main()
$ time python test.py
real 0m0.793s
Теперь посмотрим с numpy:
import numpy
def main():
for i in xrange(2560000):
a = numpy.array([0.0, 0.0, 0.0])
main()
$ time python test.py
real 0m39.338s
Священные циклы CPU бэтмен!
Использование numpy.zeros(3)
улучшает, но все же недостаточно IMHO
$ time python test.py
real 0m5.610s
user 0m5.449s
sys 0m0.070s
numpy.version.version = '1.5.1'
Если вам интересно, пропустили ли создание списка для оптимизации в первом примере, это не так:
5 19 LOAD_CONST 2 (0.0)
22 LOAD_CONST 2 (0.0)
25 LOAD_CONST 2 (0.0)
28 BUILD_LIST 3
31 STORE_FAST 1 (a)
Ответы
Ответ 1
Numpy оптимизирован для больших объемов данных. Дайте ему крошечный массив длиной 3 и, неудивительно, он плохо работает.
Рассмотрим отдельный тест
import timeit
reps = 100
pythonTest = timeit.Timer('a = [0.] * 1000000')
numpyTest = timeit.Timer('a = numpy.zeros(1000000)', setup='import numpy')
uninitialised = timeit.Timer('a = numpy.empty(1000000)', setup='import numpy')
# empty simply allocates the memory. Thus the initial contents of the array
# is random noise
print 'python list:', pythonTest.timeit(reps), 'seconds'
print 'numpy array:', numpyTest.timeit(reps), 'seconds'
print 'uninitialised array:', uninitialised.timeit(reps), 'seconds'
И вывод
python list: 1.22042918205 seconds
numpy array: 1.05412316322 seconds
uninitialised array: 0.0016028881073 seconds
Казалось бы, именно обнуление массива занимает все время для numpy. Поэтому, если вам не нужен инициализированный массив, попробуйте использовать пустой.
Ответ 2
Holy CPU cycles batman!
, действительно.
Но, пожалуйста, рассмотрите что-то очень фундаментальное, связанное с numpy
; функциональной функциональности с линейной алгеброй (например, random numbers
или singular value decomposition
). Теперь рассмотрим эти скелетно простые вычисления:
In []: A= rand(2560000, 3)
In []: %timeit rand(2560000, 3)
1 loops, best of 3: 296 ms per loop
In []: %timeit u, s, v= svd(A, full_matrices= False)
1 loops, best of 3: 571 ms per loop
и, пожалуйста, поверьте мне, что этот вид производительности не будет сильно избит любым доступным пакетом.
Итак, пожалуйста, опишите вашу реальную проблему, и я попытаюсь найти достойное решение numpy
для этого.
Update:
Вот лишь простой код для пересечения сферы луча:
import numpy as np
def mag(X):
# magnitude
return (X** 2).sum(0)** .5
def closest(R, c):
# closest point on ray to center and its distance
P= np.dot(c.T, R)* R
return P, mag(P- c)
def intersect(R, P, h, r):
# intersection of rays and sphere
return P- (h* (2* r- h))** .5* R
# set up
c, r= np.array([10, 10, 10])[:, None], 2. # center, radius
n= 5e5
R= np.random.rand(3, n) # some random rays in first octant
R= R/ mag(R) # normalized to unit length
# find rays which will intersect sphere
P, b= closest(R, c)
wi= b<= r
# and for those which will, find the intersection
X= intersect(R[:, wi], P[:, wi], r- b[wi], r)
По-видимому, мы правильно вычислили:
In []: allclose(mag(X- c), r)
Out[]: True
И некоторые тайминги:
In []: % timeit P, b= closest(R, c)
10 loops, best of 3: 93.4 ms per loop
In []: n/ 0.0934
Out[]: 5353319 #=> more than 5 million detection of possible intersections/ s
In []: %timeit X= intersect(R[:, wi], P[:, wi], r- b[wi])
10 loops, best of 3: 32.7 ms per loop
In []: X.shape[1]/ 0.0327
Out[]: 874037 #=> almost 1 million actual intersections/ s
Эти тайминги выполняются с очень скромной машиной. С современной машиной можно ожидать значительного ускорения.
Во всяком случае, это всего лишь короткая демонстрация того, как код с numpy
.
Ответ 3
Поздний ответ, но может быть важным для других зрителей.
Эта проблема была рассмотрена в проекте kwant.
Действительно, небольшие массивы не оптимизированы в numpy, и довольно часто малые массивы - именно то, что вам нужно.
В этом отношении они создали замену для небольших массивов, которые ведут себя и сосуществуют с массивами numpy (любая не реализованная операция в новом типе данных обрабатывается numpy).
Вы должны изучить этот проект:
https://pypi.python.org/pypi/tinyarray/1.0.5
основная цель - вести себя хорошо для небольших массивов. Конечно, некоторые из самых причудливых вещей, которые вы можете сделать с numpy, не поддерживаются этим. Но, по-видимому, число ваших запросов.
Я сделал несколько небольших тестов:
питон
Я добавил импорт numpy для правильного времени загрузки.
import numpy
def main():
for i in xrange(2560000):
a = [0.0, 0.0, 0.0]
main()
NumPy
import numpy
def main():
for i in xrange(2560000):
a = numpy.array([0.0, 0.0, 0.0])
main()
Numpy-ноль
import numpy
def main():
for i in xrange(2560000):
a = numpy.zeros((3,1))
main()
tinyarray
import numpy,tinyarray
def main():
for i in xrange(2560000):
a = tinyarray.array([0.0, 0.0, 0.0])
main()
tinyarray-ноль
import numpy,tinyarray
def main():
for i in xrange(2560000):
a = tinyarray.zeros((3,1))
main()
Я запустил это:
for f in python numpy numpy_zero tiny tiny_zero ; do
echo $f
for i in `seq 5` ; do
time python ${f}_test.py
done
done
И получил:
python
python ${f}_test.py 0.31s user 0.02s system 99% cpu 0.339 total
python ${f}_test.py 0.29s user 0.03s system 98% cpu 0.328 total
python ${f}_test.py 0.33s user 0.01s system 98% cpu 0.345 total
python ${f}_test.py 0.31s user 0.01s system 98% cpu 0.325 total
python ${f}_test.py 0.32s user 0.00s system 98% cpu 0.326 total
numpy
python ${f}_test.py 2.79s user 0.01s system 99% cpu 2.812 total
python ${f}_test.py 2.80s user 0.02s system 99% cpu 2.832 total
python ${f}_test.py 3.01s user 0.02s system 99% cpu 3.033 total
python ${f}_test.py 2.99s user 0.01s system 99% cpu 3.012 total
python ${f}_test.py 3.20s user 0.01s system 99% cpu 3.221 total
numpy_zero
python ${f}_test.py 1.04s user 0.02s system 99% cpu 1.075 total
python ${f}_test.py 1.08s user 0.02s system 99% cpu 1.106 total
python ${f}_test.py 1.04s user 0.02s system 99% cpu 1.065 total
python ${f}_test.py 1.03s user 0.02s system 99% cpu 1.059 total
python ${f}_test.py 1.05s user 0.01s system 99% cpu 1.064 total
tiny
python ${f}_test.py 0.93s user 0.02s system 99% cpu 0.955 total
python ${f}_test.py 0.98s user 0.01s system 99% cpu 0.993 total
python ${f}_test.py 0.93s user 0.02s system 99% cpu 0.953 total
python ${f}_test.py 0.92s user 0.02s system 99% cpu 0.944 total
python ${f}_test.py 0.96s user 0.01s system 99% cpu 0.978 total
tiny_zero
python ${f}_test.py 0.71s user 0.03s system 99% cpu 0.739 total
python ${f}_test.py 0.68s user 0.02s system 99% cpu 0.711 total
python ${f}_test.py 0.70s user 0.01s system 99% cpu 0.721 total
python ${f}_test.py 0.70s user 0.02s system 99% cpu 0.721 total
python ${f}_test.py 0.67s user 0.01s system 99% cpu 0.687 total
Теперь эти тесты (как уже указывалось) не лучшие тесты. Тем не менее, они все еще показывают, что tinyarray лучше подходит для небольших массивов.
Другим фактом является то, что наиболее распространенные операции должны выполняться быстрее с помощью tinyarray. Таким образом, это может иметь лучшие преимущества использования, чем просто создание данных.
Я никогда не пробовал его в полноценном проекте, но kwant использует его
Ответ 4
Конечно, numpy потребляет больше времени в этом случае, поскольку: a = np.array([0.0, 0.0, 0.0])
<= ~ = > a = [0.0, 0.0, 0.0]; a = np.array(a)
, это заняло два шага. Но numpy-массив имеет много хороших качеств, его высокая скорость может быть видна в операциях на них, а не в их создании. Часть моих личных мыслей:).