Równoległe pętle Pythona z tablicami numpy i dzieloną pamięcią
Znam kilka pytań i odpowiedzi na ten temat, ale nie znalazłem satysfakcjonującej odpowiedzi na ten konkretny problem:
Jaki jest najprostszy sposób na prostą równoległość współdzielonej pamięci pętli Pythona, gdzie tablice numpy są manipulowane przez funkcje numpy/scipy?
Nie szukam najskuteczniejszego sposobu, chciałem tylko czegoś prostego do wdrożenia, co nie wymaga znaczącego przepisywania, gdy pętla nie jest uruchamiana równolegle. Tak jak OpenMP implementuje w językach niższego poziomu.
Najlepszą odpowiedzią jaką widziałem w tym zakresie jest Ta , ale jest to dość niezgrabny sposób, który wymaga wyrażenia pętli w funkcję, która zajmuje jeden argument, kilka linii dzielonej tablicy konwersji crud, wydaje się wymagać, aby funkcja równoległa była wywoływana z __main__
, i nie wydaje się działać dobrze z interaktywnego monitu (gdzie spędzam dużo czasu).
Z całą prostotą Pythona jest to naprawdę najlepszy sposób na parellelise pętli? Naprawdę? Jest to coś trywialnego do paraleli w OpenMP Moda.
Uważnie przeczytałem nieprzezroczystą dokumentację modułu wieloprocesorowego, tylko po to, aby dowiedzieć się, że jest on tak ogólny, że wydaje się odpowiedni do wszystkiego, oprócz prostej równoległości pętli. Nie interesuje mnie zakładanie menedżerów, Proxy, rury itp. Mam tylko prostą pętlę, w pełni równoległą, która nie ma żadnej komunikacji między zadaniami. Korzystanie z MPI do równoległego prosta sytuacja wydaje się przesadą, nie wspominając o tym, że w tym przypadku byłaby nieefektywna pamięciowo.
Nie miałem czasu, aby dowiedzieć się o mnogości różnych pakietów współdzielonej pamięci dla Pythona, ale zastanawiałem się, czy ktoś ma większe doświadczenie w tym zakresie i może mi pokazać prostszy sposób. Proszę nie sugerować technik optymalizacji seryjnej, takich jak Cython (już go używam), lub używania funkcji numpy/scipy równoległych, takich jak BLAS (mój przypadek jest bardziej ogólny i bardziej równoległy).
3 answers
Z obsługą równoległą Cython:
# asd.pyx
from cython.parallel cimport prange
import numpy as np
def foo():
cdef int i, j, n
x = np.zeros((200, 2000), float)
n = x.shape[0]
for i in prange(n, nogil=True):
with gil:
for j in range(100):
x[i,:] = np.cos(x[i,:])
return x
Na maszynie dwurdzeniowej:
$ cython asd.pyx
$ gcc -fPIC -fopenmp -shared -o asd.so asd.c -I/usr/include/python2.7
$ export OMP_NUM_THREADS=1
$ time python -c 'import asd; asd.foo()'
real 0m1.548s
user 0m1.442s
sys 0m0.061s
$ export OMP_NUM_THREADS=2
$ time python -c 'import asd; asd.foo()'
real 0m0.602s
user 0m0.826s
sys 0m0.075s
To działa równolegle, ponieważ np.cos
(jak inne ufuncs) uwalnia GIL.
Jeśli chcesz używać tego interaktywnie:
# asd.pyxbdl
def make_ext(modname, pyxfilename):
from distutils.extension import Extension
return Extension(name=modname,
sources=[pyxfilename],
extra_link_args=['-fopenmp'],
extra_compile_args=['-fopenmp'])
And (remove asd.so
and asd.c
first):
>>> import pyximport
>>> pyximport.install(reload_support=True)
>>> import asd
>>> q1 = asd.foo()
# Go to an editor and change asd.pyx
>>> reload(asd)
>>> q2 = asd.foo()
Więc tak, w niektórych przypadkach można równoległe za pomocą wątków. OpenMP jest tylko fantazyjnym opakowaniem do tworzenia wątków, dlatego Cython jest potrzebny tylko tutaj dla łatwiejszej składni. Bez Cythona możesz użyj modułu threading
- - - działa podobnie jak multiprocessing( i prawdopodobnie bardziej solidnie), ale nie musisz robić nic specjalnego, aby zadeklarować tablice jako pamięć współdzieloną.
***
I kolejny ewentualnie przydatny link zeskrobany z innych odpowiedzi Stackoverflow - - - kolejny interfejs do multiprocessingu: http://packages.python.org/joblib/parallel.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-26 22:12:37
Użycie operacji mapowania (w tym przypadku multiprocessing.Pool.map()
) jest mniej więcej kanonicznym sposobem sparaliżowania pętli na pojedynczej maszynie. Dopóki wbudowany map()
nie zostanie kiedykolwiek sparaliżowany.
Przegląd różnych możliwości można znaleźć tutaj .
Możesz używać openmp z Pythonem (a raczej cythonem), ale nie wygląda to łatwo.
IIRC, chodzi o to, czy uruchamianie tylko rzeczy wieloprocesorowych z {[2] } jest koniecznością z powodu zgodność z systemem Windows. Ponieważ windows nie posiada fork()
, uruchamia nowy interpreter Pythona i musi zaimportować do niego kod.
Edit
Numpy może sparaliżować niektóre operacje, takie jak dot()
, vdot()
i innerproduct()
, Po skonfigurowaniu z dobrą wielowątkową biblioteką BLAS, jak np. OpenBLAS. (Patrz również to pytanie .)
Ponieważ operacje tablicy numpy są głównie wykonywane przez element, wydaje się możliwe równoległe ich zestawianie. Ale to obejmowałoby konfigurowanie segmentu pamięci współdzielonej dla obiektów Pythona lub dzielenie tablic na kawałki i przekazywanie ich różnym procesom, w przeciwieństwie do tego, co robi multiprocessing.Pool
. Bez względu na to, jakie podejście zostanie podjęte, będzie to wymagało pamięci i przetwarzania narzutu, aby zarządzać tym wszystkim. Trzeba by przeprowadzić rozległe testy, aby zobaczyć, dla których rozmiarów macierzy faktycznie byłoby to warte wysiłku. Wyniki tych testów prawdopodobnie będą się znacznie różnić w zależności od architektury sprzętu, systemu operacyjnego i ilość pamięci RAM.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:33:41
.map () metoda klasy mathDict( ) w ParallelRegression robi dokładnie to, czego szukasz w dwóch liniach kodu, które powinny być bardzo łatwe w interaktywnym monicie. Wykorzystuje on true multiprocessing, więc wymóg, aby funkcja była uruchamiana równolegle, jest nieunikniony, ale zapewnia to łatwy sposób na zapętlenie macierzy w pamięci współdzielonej z wielu procesów.
Powiedzmy, że masz funkcję ogórkową:
def sum_row( matrix, row ):
return( sum( matrix[row,:] ) )
Wtedy ty wystarczy utworzyć obiekt mathDict () reprezentujący go i użyć mathDict ().Mapa ():
matrix = np.array( [i for i in range( 24 )] ).reshape( (6, 4) )
RA, MD = mathDictMaker.fromMatrix( matrix, integer=True )
res = MD.map( [(i,) for i in range( 6 )], sum_row, ordered=True )
print( res )
# [6, 22, 38, 54, 70, 86]
Dokumentacja (link powyżej) wyjaśnia, jak przekazać kombinację argumentów pozycyjnych i słów kluczowych do funkcji, w tym samą macierz w dowolnej pozycji lub jako argument słowa kluczowego. Powinno to umożliwić Ci korzystanie z praktycznie każdej funkcji, którą już napisałeś, bez jej modyfikowania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-22 11:34:52