Параллелизация операции вектора 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 )
или есть лучший способ сделать это?
Ответы
Ответ 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
- лучший, самый безопасный метод многопоточного кода...
Надеюсь, это поможет.
Ответ 3
SciPy действительно имеет довольно хорошую рецензию на эту тему здесь: http://wiki.scipy.org/ParallelProgramming
Изменение: мертвая ссылка, теперь можно найти по адресу:http://scipy-cookbook.readthedocs.io/items/ParallelProgramming.html