Почему numpy.array такой медленный? - PullRequest
19 голосов
/ 03 июля 2011

Я сбит с толку этим

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

Holy 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)

Ответы [ 4 ]

37 голосов
/ 03 июля 2011

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

Казалось бы, это обнуление массива, которое все время занимает пустышку. Поэтому, если вам не требуется инициализация массива, попробуйте использовать пустой.

4 голосов
/ 03 июля 2011

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(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'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.

2 голосов
/ 28 апреля 2014

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

Эта проблема также рассматривалась в проекте 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 использует его

0 голосов
/ 08 декабря 2017

Конечно, numpy требует больше времени в этом случае, так как: a = np.array([0.0, 0.0, 0.0]) <= ~ => a = [0.0, 0.0, 0.0]; a = np.array(a), потребовалось два шага.Но у numpy-массива есть много хороших качеств, его высокая скорость проявляется в операциях над ними, а не в их создании.Часть моих личных мыслей:).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...