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

Author: Community, 2012-10-25

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ą.

[[9]} jednak nie wszystkie operacje wypuszczają GIL, więc YMMV do wykonania.
***

I kolejny ewentualnie przydatny link zeskrobany z innych odpowiedzi Stackoverflow - - - kolejny interfejs do multiprocessingu: http://packages.python.org/joblib/parallel.html

 16
Author: pv.,
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.

 2
Author: Roland Smith,
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.

 0
Author: RichardB,
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