Czy procesy potomne powstały w wyniku współdzielonych obiektów wieloprocesowych utworzonych wcześniej w programie?
Mam następującą konfigurację:
do_some_processing(filename):
for line in file(filename):
if line.split(',')[0] in big_lookup_object:
# something here
if __name__ == '__main__':
big_lookup_object = marshal.load('file.bin')
pool = Pool(processes=4)
print pool.map(do_some_processing, glob.glob('*.data'))
Ładuję duży obiekt do pamięci, a następnie tworzę pulę pracowników, którzy muszą z niego korzystać. Duży obiekt jest dostępny tylko do odczytu, nie muszę przekazywać jego modyfikacji między procesami.
Moje pytanie brzmi: czy duży obiekt jest ładowany do pamięci współdzielonej, tak jak by to było, gdybym uruchomił proces w unix / c, czy też każdy proces ładuje własną kopię dużego obiektu?
Aktualizacja: aby wyjaśnić dalej - big_lookup_object jest współdzielonym obiektem wyszukiwania. Nie muszę tego dzielić i przetwarzać osobno. Muszę zachować jedną kopię. Praca, której potrzebuję, aby go podzielić, polega na czytaniu wielu innych dużych plików i sprawdzaniu elementów w tych dużych plikach względem obiektu wyszukiwania.
Dalsza aktualizacja: baza danych jest dobrym rozwiązaniem, memcached może być lepszym rozwiązaniem, a plik na dysku (półka lub dbm) może być jeszcze lepszy. W tym pytaniu byłem szczególnie zainteresowany rozwiązaniem w pamięci. Jako ostateczne rozwiązanie użyję hadoopa, ale chciałem sprawdzić, czy mogę również mieć lokalną wersję w pamięci.
źródło
marshal.load
rodzica i każde dziecko (każdy proces importuje moduł).Odpowiedzi:
„Czy procesy potomne powstały w wyniku współdzielonych obiektów wieloprocesowych utworzonych wcześniej w programie?”
Nie (python przed 3.8) i tak w 3.8 ( https://docs.python.org/3/library/multiprocessing.shared_memory.html#module-multiprocessing.shared_memory )
Procesy mają niezależną przestrzeń pamięci.
Rozwiązanie 1
Aby jak najlepiej wykorzystać dużą strukturę z dużą liczbą pracowników, zrób to.
Zapisz każdego pracownika jako „filtr” - odczytuje wyniki pośrednie ze standardowego wejścia, działa, zapisuje wyniki pośrednie na standardowe wyjście.
Połącz wszystkich pracowników jako rurociąg:
Każdy proces czyta, pracuje i pisze.
Jest to niezwykle wydajne, ponieważ wszystkie procesy działają jednocześnie. Zapisy i odczyty przechodzą bezpośrednio przez bufory współdzielone między procesami.
Rozwiązanie 2
W niektórych przypadkach masz bardziej złożoną strukturę - często strukturę „rozłożoną”. W takim przypadku masz rodzica z kilkoma dziećmi.
Rodzic otwiera dane źródłowe. Rodzic rozwidla wiele dzieci.
Rodzic czyta źródło, wypuszcza części źródła do każdego współbieżnie działającego dziecka.
Kiedy rodzic dojdzie do końca, zamknij rurkę. Dziecko otrzymuje koniec pliku i kończy normalnie.
Części dziecięce są przyjemne w pisaniu, ponieważ każde dziecko po prostu czyta
sys.stdin
.Rodzic ma trochę wymyślnej pracy nóg przy odradzaniu wszystkich dzieci i prawidłowym utrzymywaniu rur, ale nie jest tak źle.
Fan-in to odwrotna konstrukcja. Wiele niezależnie działających procesów musi przeplatać swoje dane wejściowe do wspólnego procesu. Kolektor nie jest tak łatwy do napisania, ponieważ musi czytać z wielu źródeł.
Czytanie z wielu nazwanych potoków jest często wykonywane za pomocą
select
modułu, aby zobaczyć, które potoki mają oczekujące dane wejściowe.Rozwiązanie 3
Wspólne wyszukiwanie to definicja bazy danych.
Rozwiązanie 3A - załaduj bazę danych. Pozwól pracownikom przetwarzać dane w bazie danych.
Rozwiązanie 3B - utwórz bardzo prosty serwer przy użyciu werkzeug (lub podobnego), aby zapewnić aplikacje WSGI, które odpowiadają na HTTP GET, aby pracownicy mogli wysyłać zapytania do serwera.
Rozwiązanie 4
Współdzielony obiekt systemu plików. Unix OS oferuje współdzielone obiekty pamięci. Są to tylko pliki, które są mapowane do pamięci, dzięki czemu zamiana we / wy jest wykonywana zamiast bardziej konwencjonalnych odczytów buforowanych.
Możesz to zrobić z kontekstu Pythona na kilka sposobów
Napisz program startowy, który (1) rozbija twój oryginalny gigantyczny obiekt na mniejsze obiekty i (2) uruchamia pracowników, każdy z mniejszym obiektem. Mniejsze obiekty mogą być piklowanymi obiektami Pythona, aby zaoszczędzić trochę czasu na odczyt plików.
Napisz program startowy, który (1) czyta twój oryginalny gigantyczny obiekt i zapisuje plik ze strukturą strony, zakodowany bajtowo, używając
seek
operacji, aby zapewnić, że poszczególne sekcje są łatwe do znalezienia za pomocą prostych wyszukiwań. To właśnie robi silnik bazy danych - dzieli dane na strony, ułatwiając zlokalizowanie każdej strony za pomocą plikuseek
.Przywołaj pracowników mających dostęp do tego dużego pliku o strukturze strony. Każdy pracownik może szukać odpowiednich części i tam wykonywać swoją pracę.
źródło
Czy procesy potomne powstały w wyniku współdzielonych obiektów wieloprocesowych utworzonych wcześniej w programie?
To zależy. W przypadku zmiennych globalnych tylko do odczytu można to często uznać za tak (oprócz zajętej pamięci), w przeciwnym razie nie powinno.
Dokumentacja multiprocessingu mówi:
Better to inherit than pickle/unpickle
Explicitly pass resources to child processes
Global variables
Przykład
W systemie Windows (pojedynczy procesor):
Z
sleep
:Bez
sleep
:źródło
z
nie jest udostępniana. To w ten sposób odpowiada na pytanie: „nie, przynajmniej pod Windows zmienna nadrzędna nie jest dzielona między dzieci”.z
przypadku), można je uznać za udostępnione.S.Lott ma rację. Skróty wieloprocesorowe Pythona skutecznie zapewniają oddzielny, zduplikowany fragment pamięci.
W większości systemów * nix użycie wywołania niższego poziomu
os.fork()
w rzeczywistości da ci pamięć typu „kopiuj przy zapisie”, o której myślisz. AFAIK, w teorii, w najbardziej uproszczonych programach można było czytać te dane bez ich powielania.Jednak w przypadku interpretera Pythona sprawy nie są takie proste. Dane obiektu i metadane są przechowywane w tym samym segmencie pamięci, więc nawet jeśli obiekt nigdy się nie zmienia, coś w rodzaju licznika odniesienia dla zwiększanego obiektu spowoduje zapis do pamięci, a tym samym kopię. Prawie każdy program w języku Python, który robi więcej niż „print 'hello'”, spowoduje zwiększenie liczby odwołań, więc prawdopodobnie nigdy nie zdasz sobie sprawy z korzyści płynących z kopiowania przy zapisie.
Nawet gdyby komuś udało się zhakować rozwiązanie pamięci współdzielonej w Pythonie, próba skoordynowania czyszczenia pamięci między procesami byłaby prawdopodobnie dość bolesna.
źródło
Jeśli pracujesz pod Uniksem, mogą dzielić ten sam obiekt, ze względu na to, jak działa fork (tj. Procesy potomne mają oddzielną pamięć, ale jest to kopiowanie przy zapisie, więc może być współdzielone, o ile nikt go nie modyfikuje). Wypróbowałem następujące:
i otrzymałem następujący wynik:
Oczywiście nie dowodzi to, że kopia nie została utworzona, ale powinieneś być w stanie to sprawdzić w swojej sytuacji, patrząc na wynik,
ps
aby zobaczyć, ile rzeczywistej pamięci używa każdy podproces.źródło
Różne procesy mają różną przestrzeń adresową. Jak uruchamianie różnych instancji tłumacza. Do tego służy IPC (komunikacja międzyprocesowa).
W tym celu można użyć kolejek lub potoków. Możesz także użyć rpc over tcp, jeśli chcesz później rozprowadzić procesy w sieci.
http://docs.python.org/dev/library/multiprocessing.html#exchanging-objects-between-processes
źródło
Nie jest to bezpośrednio związane z przetwarzaniem wieloprocesowym jako takim, ale z twojego przykładu wydaje się, że możesz po prostu użyć modułu półki lub czegoś podobnego. Czy „big_lookup_object” naprawdę musi być całkowicie w pamięci?
źródło
Nie, ale możesz załadować swoje dane jako proces podrzędny i pozwolić mu na udostępnianie swoich danych innym dzieciom. patrz poniżej.
źródło
W przypadku platformy Linux / Unix / MacOS forkmap jest szybkim rozwiązaniem.
źródło