Почему 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) 

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. Поэтому, если вам не нужен инициализированный массив, попробуйте использовать пустой.

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 .

Обновить:
Вот простой код для пересечения сферы луча:

 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(cT, 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's 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 .

Поздний ответ, но может быть важным для других зрителей.

Эта проблема была рассмотрена и в проекте kwant . Действительно, небольшие массивы не оптимизированы в numpy, и довольно часто малые массивы – именно то, что вам нужно.

В связи с этим они создали замену для небольших массивов, которые ведут себя и сосуществуют с массивами numpy (любая нереализованная операция в новом типе данных обрабатывается numpy).

Вы должны изучить этот проект:
https://pypi.python.org/pypi/tinyarray/1.0.5
основная цель – вести себя хорошо для небольших массивов. Конечно, некоторые из самых причудливых вещей, которые вы можете сделать с numpy, не поддерживаются этим. Но, по-видимому, ваш запрос.

Я сделал несколько небольших тестов:

питон

Я добавил numpy import, чтобы получить правильное время загрузки

 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 использует его