Подтвердить что ты не робот

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

Ответ 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-массив имеет много хороших качеств, его высокая скорость может быть видна в операциях на них, а не в их создании. Часть моих личных мыслей:).