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

Параллелизация операции вектора Numy

Используйте, например, numpy.sin()

Следующий код вернет значение синуса для каждого значения массива a:

import numpy
a = numpy.arange( 1000000 )
result = numpy.sin( a )

Но моя машина имеет 32 ядра, поэтому я хотел бы использовать их. (Накладные расходы могут оказаться нецелесообразными для чего-то вроде numpy.sin(), но функция, которую я действительно хочу использовать, довольно сложна, и я буду работать с огромным количеством данных.)

Это лучший (читай: самый умный или быстрый) метод:

from multiprocessing import Pool
if __name__ == '__main__':
    pool = Pool()
    result = pool.map( numpy.sin, a )

или есть лучший способ сделать это?

4b9b3361

Ответ 1

Существует лучший способ: numexpr

Немного переформулированы с их главной страницы:

Это многопоточная VM, написанная на C, которая анализирует выражения, переписывает их более эффективно и компилирует их на лету в код, который приближается к оптимальной параллельной производительности как для оперативной памяти, так и для ограниченных операций.

Например, на моем 4-ядерном компьютере оценка синуса чуть меньше, чем в 4 раза быстрее, чем numpy.

In [1]: import numpy as np
In [2]: import numexpr as ne
In [3]: a = np.arange(1000000)
In [4]: timeit ne.evaluate('sin(a)')
100 loops, best of 3: 15.6 ms per loop    
In [5]: timeit np.sin(a)
10 loops, best of 3: 54 ms per loop

Документация, в том числе поддерживаемых функций здесь. Вам нужно будет проверить или предоставить нам дополнительную информацию, чтобы узнать, может ли ваша более сложная функция быть оценена numexpr.

Ответ 2

Хорошо, это интересно, если вы выполните следующие команды:

import numpy
from multiprocessing import Pool
a = numpy.arange(1000000)    
pool = Pool(processes = 5)
result = pool.map(numpy.sin, a)

UnpicklingError: NEWOBJ class argument has NULL tp_new

не ожидал этого, так что происходит, ну:

>>> help(numpy.sin)
   Help on ufunc object:

sin = class ufunc(__builtin__.object)
 |  Functions that operate element by element on whole arrays.
 |  
 |  To see the documentation for a specific ufunc, use np.info().  For
 |  example, np.info(np.sin).  Because ufuncs are written in C
 |  (for speed) and linked into Python with NumPy ufunc facility,
 |  Python help() function finds this page whenever help() is called
 |  on a ufunc.

yep numpy.sin реализован в c как таковой, вы не можете использовать его напрямую при многопроцессорности.

поэтому мы должны обернуть его другой функцией

перфорация:

import time
import numpy
from multiprocessing import Pool

def numpy_sin(value):
    return numpy.sin(value)

a = numpy.arange(1000000)
pool = Pool(processes = 5)

start = time.time()
result = numpy.sin(a)
end = time.time()
print 'Singled threaded %f' % (end - start)
start = time.time()
result = pool.map(numpy_sin, a)
pool.close()
pool.join()
end = time.time()
print 'Multithreaded %f' % (end - start)


$ python perf.py 
Singled threaded 0.032201
Multithreaded 10.550432

ничего себе, тоже не ожидал, что у нас есть пара проблем для стартеров, мы используем функцию python, даже если это просто оболочка против чистой c-функции, а также накладные расходы на копирование значений, многопроцессорность на default не делит данные, так как каждое значение должно быть скопировано обратно/вперед.

Обратите внимание, что если правильно сегментировать наши данные:

import time
import numpy
from multiprocessing import Pool

def numpy_sin(value):
    return numpy.sin(value)

a = [numpy.arange(100000) for _ in xrange(10)]
pool = Pool(processes = 5)

start = time.time()
result = numpy.sin(a)
end = time.time()
print 'Singled threaded %f' % (end - start)
start = time.time()
result = pool.map(numpy_sin, a)
pool.close()
pool.join()
end = time.time()
print 'Multithreaded %f' % (end - start)

$ python perf.py 
Singled threaded 0.150192
Multithreaded 0.055083

Итак, что мы можем извлечь из этого, многопроцессорность велик, но мы всегда должны проверять и сравнивать ее иногда быстрее, а иногда и медленнее, в зависимости от того, как она используется...

Предположим, вы не используете numpy.sin, а еще одну функцию, которую я бы рекомендовал вам сначала убедиться, что действительно многопроцессорное ускорение вычислений, возможно, накладные расходы на копирование значений назад/вперед могут повлиять на вас.

В любом случае я также полагаю, что использование pool.map - лучший, самый безопасный метод многопоточного кода...

Надеюсь, это поможет.