Mam 60 GB SciPy Array (Matrix), które muszę udostępniać między 5+ multiprocessing
Process
obiektami. Widziałem numpy-sharedmem i przeczytałem tę dyskusję na liście SciPy. Wydaje się, że są dwa podejścia - numpy-sharedmem
i używając a multiprocessing.RawArray()
oraz mapując NumPy dtype
s na ctype
s. numpy-sharedmem
Wydaje się , że jest to właściwy sposób, ale nie widziałem jeszcze dobrego przykładu referencyjnego. Nie potrzebuję żadnych blokad, ponieważ tablica (właściwie macierz) będzie tylko do odczytu. Teraz, ze względu na jego rozmiar, chciałbym uniknąć kopii. To brzmi jak poprawna metoda jest stworzenie tylko kopię tablicy jako sharedmem
tablicy, a następnie przekazać je do Process
obiektów? Kilka szczegółowych pytań:
Jaki jest najlepszy sposób przekazania uchwytów pamięci współdzielonej do podrzędnych
Process()
? Czy potrzebuję kolejki tylko do przekazywania jednej tablicy? Czy fajka byłaby lepsza? Czy mogę po prostu przekazać to jako argument doProcess()
init podklasy (gdzie zakładam, że jest wytrawiony)?W dyskusji, którą połączyłem powyżej, jest wzmianka o
numpy-sharedmem
braku bezpieczeństwa 64-bitowego? Zdecydowanie używam niektórych struktur, które nie są 32-bitowe adresowalne.Czy są jakieś kompromisy w tym
RawArray()
podejściu? Wolniej, buggier?Czy potrzebuję mapowania ctype-to-dtype dla metody numpy-sharedmem?
Czy ktoś ma przykład, jak jakiś kod OpenSource to robi? Jestem bardzo praktyczny i ciężko jest mi to udać bez żadnego dobrego przykładu.
Jeśli mogę podać dodatkowe informacje, które pomogą wyjaśnić to innym, skomentuj, a dodam. Dzięki!
To musi działać na Ubuntu Linux i może Mac OS, ale przenośność nie jest wielkim problemem.
multiprocessing
się wykonania kopii całości dla każdego procesu.target_function
). System operacyjny będzie kopiował części pamięci rodzica do przestrzeni pamięci dziecka tylko po modyfikacji.Odpowiedzi:
@Velimir Mlaker dał świetną odpowiedź. Pomyślałem, że mógłbym dodać kilka komentarzy i mały przykład.
(Nie mogłem znaleźć dużo dokumentacji na współdzielonej pamięci - to są wyniki moich własnych eksperymentów.)
target
i . Jest to potencjalnie lepsze niż używanie zmiennej globalnej.args
Process
Przykład
#!/usr/bin/env python from multiprocessing import Process import sharedmem import numpy def do_work(data, start): data[start] = 0; def split_work(num): n = 20 width = n/num shared = sharedmem.empty(n) shared[:] = numpy.random.rand(1, n)[0] print "values are %s" % shared processes = [Process(target=do_work, args=(shared, i*width)) for i in xrange(num)] for p in processes: p.start() for p in processes: p.join() print "values are %s" % shared print "type is %s" % type(shared[0]) if __name__ == '__main__': split_work(4)
Wynik
values are [ 0.81397784 0.59667692 0.10761908 0.6736734 0.46349645 0.98340718 0.44056863 0.10701816 0.67167752 0.29158274 0.22242552 0.14273156 0.34912309 0.43812636 0.58484507 0.81697513 0.57758441 0.4284959 0.7292129 0.06063283] values are [ 0. 0.59667692 0.10761908 0.6736734 0.46349645 0. 0.44056863 0.10701816 0.67167752 0.29158274 0. 0.14273156 0.34912309 0.43812636 0.58484507 0. 0.57758441 0.4284959 0.7292129 0.06063283] type is <type 'numpy.float64'>
To powiązane pytanie może być przydatne.
źródło
Jeśli korzystasz z Linuksa (lub dowolnego systemu zgodnego z POSIX), możesz zdefiniować tę tablicę jako zmienną globalną.
multiprocessing
używafork()
w systemie Linux, kiedy rozpoczyna nowy proces potomny. Nowo utworzony proces potomny automatycznie współdzieli pamięć ze swoim rodzicem, o ile jej nie zmienia ( mechanizm kopiowania przy zapisie ).Ponieważ mówisz „Nie potrzebuję żadnych blokad, ponieważ tablica (właściwie macierz) będzie tylko do odczytu” skorzystanie z tego zachowania byłoby bardzo prostym, a jednocześnie niezwykle wydajnym podejściem: wszystkie procesy potomne będą miały dostęp te same dane w pamięci fizycznej podczas odczytu tej dużej tablicy numpy.
Nie przekazuj swojej tablicy
Process()
konstruktorowi, spowodujemultiprocessing
topickle
przekazanie danych dziecku, co w twoim przypadku byłoby wyjątkowo nieefektywne lub niemożliwe. W Linuksie zaraz zafork()
dzieckiem znajduje się dokładna kopia rodzica korzystająca z tej samej pamięci fizycznej, więc wszystko, co musisz zrobić, to upewnić się, że zmienna Pythona „zawierająca” macierz jest dostępna z poziomutarget
funkcji, której przekazujeszProcess()
. Zwykle można to osiągnąć za pomocą zmiennej „globalnej”.Przykładowy kod:
from multiprocessing import Process from numpy import random global_array = random.random(10**4) def child(): print sum(global_array) def main(): processes = [Process(target=child) for _ in xrange(10)] for p in processes: p.start() for p in processes: p.join() if __name__ == "__main__": main()
W systemie Windows - który nie obsługuje
fork()
-multiprocessing
używa wywołania Win32 APICreateProcess
. Tworzy całkowicie nowy proces z dowolnego pliku wykonywalnego. Dlatego w systemie Windows wymagane jest wytrawianie danych dziecku, jeśli potrzebne są dane, które zostały utworzone podczas działania rodzica.źródło
multiprocessing
lub procesy potomne mogą się zawiesić ( zazwyczaj używa 1 / n jednego procesora zamiast n procesorów) podczas wykonywania operacji algebry liniowej na wspólnej globalnej tablicy / macierzy. Znany wielowątkowe konflikt z OpenBLAS wydaje się przedłużyć do Pythonamultiprocessing
fork
do przekazywania podanych parametrówProcess
, zamiast ich serializacji? To znaczy, czy niefork
można zastosować do procesu nadrzędnego tuż przedchild
wywołaniem, tak aby wartość parametru była nadal dostępna z systemu operacyjnego? Wydawałoby się, że jest bardziej wydajna niż serializacja?fork()
nie jest dostępny w systemie Windows, zostało to stwierdzone w mojej odpowiedzi i wielokrotnie w komentarzach. Wiem, że to była twoja początkowa pytanie i odpowiedziałem jej cztery komentarze powyżej to : „Kompromis jest użycie tej samej metody przekazania parametrów na obu platformach domyślnie dla lepszej konserwacji oraz dla zapewnienia równego zachowania.”. Oba sposoby mają swoje wady i zalety, dlatego w Pythonie 3 istnieje większa elastyczność w wyborze metody przez użytkownika. Ta dyskusja nie jest produktywna bez omawiania szczegółów, czego nie powinniśmy tutaj robić.Może zainteresuje Cię mały fragment kodu, który napisałem: github.com/vmlaker/benchmark-sharedmem
Jedyny interesujący plik to
main.py
. Jest to punkt odniesienia dla numpy-sharedmem - kod po prostu przekazuje tablice (albonumpy
lubsharedmem
) do zrodzonych procesów, za pośrednictwem potoku. Pracownicy po prostusum()
korzystają z danych. Interesowało mnie tylko porównanie czasów przesyłania danych między dwoma wdrożeniami.Napisałem też inny, bardziej złożony kod: github.com/vmlaker/sherlock .
Tutaj używam modułu numpy-sharedmem do przetwarzania obrazu w czasie rzeczywistym z OpenCV - obrazy są tablicami NumPy, zgodnie z nowszym
cv2
API OpenCV . Obrazy, a właściwie ich odniesienia, są współdzielone między procesami za pośrednictwem obiektu słownika utworzonego zmultiprocessing.Manager
(w przeciwieństwie do kolejki lub potoku). W porównaniu ze zwykłymi tablicami NumPy osiągam znaczną poprawę wydajności.Rura a kolejka :
Z mojego doświadczenia wynika, że IPC z potokiem jest szybsze niż kolejka. Ma to sens, ponieważ Queue dodaje blokowanie, aby było bezpieczne dla wielu producentów / konsumentów. Rura nie. Ale jeśli masz tylko dwa procesy rozmawiające w tę iz powrotem, możesz bezpiecznie użyć potoku lub, jak czytają dokumenty:
sharedmem
bezpieczeństwo :Głównym problemem związanym z
sharedmem
modułem jest możliwość wycieku pamięci po niezręcznym zakończeniu programu. Opisano to w obszernej dyskusji tutaj . Chociaż 10 kwietnia 2011 Sturla wspomina naprawę wycieku pamięci, od tego czasu wciąż doświadczałem wycieków, używając obu repozytoriów , własnego Sturla Molden na GitHub ( github.com/sturlamolden/sharedmem-numpy ) i Chrisa Lee-Messera na Bitbucket ( bitbucket.org/cleemesser/numpy-sharedmem ).źródło
sharedmem
brzmi jak wielka sprawa. Jakieś wskazówki, jak to rozwiązać?sharedmem
modułu open source , w celach informacyjnych.Jeśli twoja tablica jest tak duża, możesz użyć
numpy.memmap
. Na przykład, jeśli masz tablicę przechowywaną na dysku, powiedzmy'test.array'
, możesz użyć jednoczesnych procesów, aby uzyskać dostęp do danych w niej nawet w trybie „zapisu”, ale sprawa jest prostsza, ponieważ potrzebujesz tylko trybu „odczytu”.Tworzenie tablicy:
a = np.memmap('test.array', dtype='float32', mode='w+', shape=(100000,1000))
Następnie możesz wypełnić tę tablicę w taki sam sposób, jak w przypadku zwykłej tablicy. Na przykład:
a[:10,:100]=1. a[10:,100:]=2.
Dane są zapisywane na dysku, gdy usuwasz zmienną
a
.Później możesz korzystać z wielu procesów, które będą uzyskiwać dostęp do danych w
test.array
:# read-only mode b = np.memmap('test.array', dtype='float32', mode='r', shape=(100000,1000)) # read and writing mode c = np.memmap('test.array', dtype='float32', mode='r+', shape=(100000,1000))
Powiązane odpowiedzi:
Praca z big data w Pythonie i Numpy, za mało pamięci RAM, jak zapisać częściowe wyniki na dysku?
Czy można zmapować nieciągłe dane na dysku do tablicy z Pythonem?
źródło
Przydatne może być również zapoznanie się z dokumentacją dla pyro, tak jakbyś mógł odpowiednio podzielić swoje zadanie, mógłbyś go użyć do wykonania różnych sekcji na różnych maszynach, a także na różnych rdzeniach tej samej maszyny.
źródło
Dlaczego nie skorzystać z wielowątkowości? Zasoby procesu głównego mogą być współdzielone przez jego wątki w sposób natywny, dlatego wielowątkowość jest oczywiście lepszym sposobem udostępniania obiektów należących do procesu głównego.
Jeśli martwisz się mechanizmem GIL w Pythonie, może możesz skorzystać z
nogil
ofnumba
.źródło