Распараллеливание векторной операции Numpy

Давайте использовать, например, 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 )

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


person user1475412    schedule 11.07.2012    source источник
comment
Если вы собираетесь использовать pool.map(), вам следует использовать math.sin, потому что он быстрее, чем numpy.sin. Ссылка: stackoverflow.com/questions / 3650194 /.   -  person Eric O Lebigot    schedule 06.01.2014
comment
Для numpy.sin в официальной вики-странице numpy / scipy говорится, что он должен работать параллельно, если вы скомпилировать numpy с включенным openmp.   -  person ziyuang    schedule 18.02.2015
comment
Вы также можете использовать Bohrium: это должно быть так же просто, как заменить первую строку на _1 _...   -  person j08lue    schedule 04.05.2016


Ответы (3)


Есть способ получше: numexpr

Немного изменен текст с главной страницы:

Это многопоточная виртуальная машина, написанная на 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.

person jorgeca    schedule 12.07.2012
comment
Я написал свой код с использованием numexpr, и он работает примерно в 6 раз быстрее, чем тот же код с использованием numpy. Большое спасибо за предложение! Теперь мне интересно, почему numexpr не так широко. Во время всех моих поисков числовых пакетов в Python я до сих пор не встречал их. Также было небольшое раздражение в том, что numexpr не поддерживает индексацию массивов, но это вряд ли было неудачей. - person user1475412; 16.07.2012
comment
Может, тогда тебе стоит проверить Theano и Cython. Theano может использовать графические процессоры, но я еще не использовал их, поэтому не могу привести вам пример. - person jorgeca; 16.07.2012
comment
Одна из причин, по которой numexpr не является более распространенным, заключается в том, что я предполагаю тот факт, что его более громоздко использовать, чем чистый NumPy (как в приведенном выше примере). Тем не менее, он действительно отлично подходит для быстрого ускорения вычислений NumPy, чем нужно, чтобы работать быстрее. - person Eric O Lebigot; 17.08.2015
comment
Я получил 30-кратное ускорение с этим! Потрясающий :) - person Phani; 28.07.2018

Что ж, это интересное замечание, если вы запустите следующие команды:

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's ufunc facility,
 |  Python's help() function finds this page whenever help() is called
 |  on a ufunc.

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

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

perf:

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, и есть также накладные расходы на копирование значений, многопроцессорная обработка по умолчанию не • обмениваться данными, поэтому каждое значение необходимо копировать взад / вперед.

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

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 - лучший и самый безопасный метод многопоточного кода ...

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

person Samy Vilar    schedule 11.07.2012
comment
Большое спасибо! Это очень информативно. На основании того, что я прочитал, я предположил, что функция Pool map() будет несколько разумно работать с данными, но я думаю, что сначала ее сегментирование имеет огромное значение. Есть ли другой способ избежать накладных расходов, связанных с копированием данных процессами? Ожидаете ли вы какой-либо разницы в производительности, если вместо этого я буду использовать math.sin()? - person user1475412; 12.07.2012
comment
Я на самом деле пробовал math.sin, и он намного медленнее, даже многопоточный, чем однопоточный numpy.sin, хотя он был быстрее (занял 6.435199с, чем многопоточный numpy.sin, который занял 10.5, вероятно, из-за того, что numpy.sin может обрабатывать массивы, ребята numpy действительно хорошо разбирается в математике;), да, есть способ использовать shared memory docs.python.org/library/ multiprocessing.html, но, пожалуйста, не используйте его довольно опасно и имеет ограниченную поддержку, или, по крайней мере, действуйте осторожно. - person Samy Vilar; 12.07.2012
comment
Если вы выполняете только чтение, то это может быть безопасно, подпроцессу нужно только отслеживать их соответствующий индекс или подмножество индексов ... - person Samy Vilar; 12.07.2012

На самом деле у SciPy есть довольно хорошая статья по этому поводу здесь: http://wiki.scipy.org/ParallelProgramming

Изменить: мертвая ссылка, теперь ее можно найти по адресу: http://scipy-cookbook.readthedocs.io/items/ParallelProgramming.html

person entropy    schedule 11.07.2012
comment
Ссылка мертвая. Это то, о чем вы имели в виду? scipy-cookbook.readthedocs.io/items/ParallelProgramming.html - person mateuszb; 14.08.2019