Chciałbym zsynchronizować mój program w Pythonie, aby mógł korzystać z wielu procesorów na maszynie, na której działa. Moja równoległość jest bardzo prosta, ponieważ wszystkie równoległe „wątki” programu są niezależne i zapisują swoje dane wyjściowe w oddzielnych plikach. Nie potrzebuję wątków do wymiany informacji, ale konieczne jest, aby wiedzieć, kiedy wątki się skończą, ponieważ niektóre etapy mojego potoku zależą od ich danych wyjściowych.
Przenośność jest ważna, ponieważ chciałbym, aby działała na dowolnej wersji Pythona na komputerach Mac, Linux i Windows. Biorąc pod uwagę te ograniczenia, który z modułów Pythona jest najbardziej odpowiedni do zaimplementowania tego? Próbuję wybrać między wątkiem, podprocesem i wieloprocesorem, które wydają się zapewniać pokrewną funkcjonalność.
Jakieś przemyślenia na ten temat? Chciałbym najprostsze przenośne rozwiązanie.
źródło
Odpowiedzi:
multiprocessing
to świetny moduł typu szwajcarskiego scyzoryka. Jest bardziej ogólny niż wątki, ponieważ można nawet wykonywać zdalne obliczenia. Dlatego jest to moduł, który sugerowałbym, abyś użył.subprocess
Moduł będzie również pozwalają na uruchomienie wielu procesów, ale okazało się, że jest mniej wygodny w użyciu niż nowego modułu wieloprocesorowej.Wątki są notorycznie subtelne, a dzięki CPython często jesteś ograniczony do jednego rdzenia, z nimi (chociaż, jak zauważono w jednym z komentarzy, Global Interpreter Lock (GIL) może być zwolniony w kodzie C wywołanym z kodu Pythona) .
Uważam, że większość funkcji wymienionych trzech modułów może być wykorzystywana w sposób niezależny od platformy. Jeśli chodzi o przenośność, zwróć uwagę, że
multiprocessing
występuje w standardzie tylko od Pythona 2.6 (choć istnieje wersja dla niektórych starszych wersji Pythona). Ale to świetny moduł!źródło
Dla mnie jest to całkiem proste:
Opcja podprocesu :
subprocess
służy do uruchamiania innych plików wykonywalnych - jest to w zasadzie opakowanie dookołaos.fork()
ios.execve()
z pewną obsługą opcjonalnej instalacji wodociągowej (konfigurowanie PIPE do i z podprocesów. Oczywiście można też zastosować inne mechanizmy komunikacji międzyprocesowej (IPC), takie jak gniazda, Posix lub Pamięć współdzielona SysV, ale będziesz ograniczony do interfejsów i kanałów IPC obsługiwanych przez programy, które wywołujesz.Zwykle używa się dowolnego narzędzia
subprocess
synchronicznie - po prostu wywołując jakieś zewnętrzne narzędzie i odczytując jego dane wyjściowe lub czekając na jego zakończenie (być może czytając jego wyniki z pliku tymczasowego lub po wysłaniu ich do jakiejś bazy danych).Jednak można stworzyć setki podprocesów i sondować je. Moja ulubiona klasa narzędziowa właśnie to robi. Największą wadą tego
subprocess
modułu jest to, że wsparcie I / O jest zazwyczaj blokując. Istnieje szkic PEP-3145, który naprawi ten problem w niektórych przyszłych wersjach Pythona 3.xi alternatywny asyncproc (ostrzeżenie, które prowadzi prosto do pobrania, a nie do jakiejkolwiek dokumentacji ani pliku README). Odkryłem również, że stosunkowo łatwo jest po prostu zaimportowaćfcntl
i bezpośrednio manipulowaćPopen
deskryptorami plików PIPE - chociaż nie wiem, czy jest to przenośne na platformy inne niż UNIX.(Aktualizacja: 7 sierpnia 2019: obsługa Python 3 dla podprocesów ayncio : podprocesy asyncio )
subprocess
prawie nie obsługuje obsługi zdarzeń ... chociaż możesz użyćsignal
modułu i zwykłych sygnałów ze starej szkoły UNIX / Linux - łagodnie zabijając procesy.Opcja przetwarzania wieloprocesowego :
multiprocessing
służy do uruchamiania funkcji w istniejącym kodzie (Python) z obsługą bardziej elastycznej komunikacji między tą rodziną procesów. W szczególności najlepiej jest budować swójmultiprocessing
IPC wokółQueue
obiektów modułu, jeśli to możliwe, ale możesz także używaćEvent
obiektów i różnych innych funkcji (z których niektóre są prawdopodobnie zbudowane wokółmmap
wsparcia na platformach, na których ta obsługa jest wystarczająca).multiprocessing
Moduł Pythona ma zapewniać interfejsy i funkcje, które są bardzo podobne do,threading
jednocześnie umożliwiając CPythonowi skalowanie przetwarzania między wieloma procesorami / rdzeniami pomimo GIL (Global Interpreter Lock). Wykorzystuje wszystkie drobnoziarniste blokowanie SMP i wysiłek związany z koherencją, który został wykonany przez programistów jądra twojego systemu operacyjnego.Opcja gwintowania :
threading
jest przeznaczony do dość wąskiego zakresu aplikacji, które są związane z operacjami we / wy (nie wymagają skalowania na wiele rdzeni procesora) i które korzystają z wyjątkowo niskiego opóźnienia i narzutu przełączania przełączania wątków (ze współdzieloną pamięcią rdzeniową) w porównaniu z procesem / przełączanie kontekstu. W systemie Linux jest to prawie pusty zestaw (czasy przełączania procesów w systemie Linux są bardzo zbliżone do przełączników wątków).threading
ma dwie główne wady w Pythonie .Jeden, oczywiście, jest specyficzny dla implementacji - głównie dotyczy CPythona. To jest GIL. W przeważającej części, większość programów CPython nie skorzystają z dostępności więcej niż dwa procesory (rdzenie) i często wydajność będzie cierpieć z niezgody blokującego GIL.
Większy problem, który nie jest specyficzny dla implementacji, polega na tym, że wątki współużytkują tę samą pamięć, programy obsługi sygnałów, deskryptory plików i niektóre inne zasoby systemu operacyjnego. Dlatego programista musi bardzo uważać na blokowanie obiektów, obsługę wyjątków i inne aspekty swojego kodu, które są zarówno subtelne, jak i mogą zabić, zablokować lub zablokować cały proces (zestaw wątków).
Dla porównania
multiprocessing
model nadaje każdemu procesowi własną pamięć, deskryptory plików itp. Awaria lub nieobsługiwany wyjątek w którymkolwiek z nich zabije tylko ten zasób, a solidna obsługa zniknięcia procesu potomnego lub rodzeństwa może być znacznie łatwiejsza niż debugowanie, izolowanie oraz naprawianie lub obejście podobnych problemów w wątkach.threading
z głównymi systemami Pythona, takimi jak NumPy , może znacznie mniej cierpieć z powodu rywalizacji GIL niż większość własnego kodu w Pythonie. Dzieje się tak, ponieważ zostały specjalnie zaprojektowane do tego; natywne / binarne części NumPy, na przykład zwolni GIL, gdy będzie to bezpieczne).Twisted opcja:
Warto również zauważyć, że Twisted oferuje kolejną alternatywę, która jest zarówno elegancka, jak i bardzo trudna do zrozumienia . Zasadniczo, ryzykując zbytnie uproszczenie do punktu, w którym fani Twisted mogą szturmować mój dom z widłami i pochodniami, Twisted zapewnia współpracę wielozadaniową opartą na zdarzeniach w ramach dowolnego (pojedynczego) procesu.
Aby zrozumieć, jak to jest możliwe, powinno się przeczytać o funkcjach
select()
(które mogą być budowane wokół select () lub poll () lub podobnych wywołań systemowych OS). Zasadniczo wszystko jest napędzane możliwością wysłania żądania uśpienia systemu operacyjnego w oczekiwaniu na jakąkolwiek aktywność na liście deskryptorów plików lub przekroczenia limitu czasu.Przebudzenie z każdego z tych wywołań
select()
jest zdarzeniem - albo takim, w którym dane wejściowe są dostępne (czytelne) w pewnej liczbie gniazd lub deskryptorów plików, albo przestrzeń buforowa staje się dostępna w innych (zapisywalnych) deskryptorach lub gniazdach, niektóre wyjątkowe warunki (TCP na przykład pozapasmowe pakiety PUSH) lub TIMEOUT.Tak więc model programowania Twisted jest zbudowany wokół obsługi tych zdarzeń, a następnie zapętlony na wynikowym „głównym” module obsługi, umożliwiając mu wysyłanie zdarzeń do twoich programów obsługi.
Osobiście myślę o nazwie Twisted jako kojarzącej się z modelem programowania ... ponieważ twoje podejście do problemu musi być w pewnym sensie „wypaczone” na lewą stronę. Zamiast wyobrażać sobie program jako serię operacji na danych wejściowych i wyjściach lub wynikach, piszesz program jako usługę lub demon i definiujesz, jak reaguje na różne zdarzenia. (W rzeczywistości podstawową "główną pętlą" programu Twisted jest (zwykle? Zawsze?) A
reactor()
).Do najważniejszych zadań przy użyciu Twisted zaangażować swój umysł skręcania wokół modelu zdarzeniami, a także unikając użycia jakichkolwiek bibliotek klas lub zestawów narzędzi, które nie są napisane współpracować w Twisted ramy. Dlatego Twisted dostarcza własne moduły do obsługi protokołu SSH, dla curses i własnych funkcji podprocesu / Popen, a także wiele innych modułów i programów obsługi protokołów, które na pierwszy rzut oka wydają się powielać rzeczy w standardowych bibliotekach Pythona.
Myślę, że warto zrozumieć Twisted na poziomie koncepcyjnym, nawet jeśli nigdy nie zamierzasz go używać. Może dać wgląd w wydajność, rywalizację i obsługę zdarzeń w twoich wątkach, przetwarzaniu wieloprocesowym, a nawet obsłudze podprocesów, a także w przetwarzaniu rozproszonym, które podejmujesz.
( Uwaga: Nowsze wersje Pythona 3.x są tym asyncio (asynchroniczne I / O) dysponuje takimi jak asynchroniczny def , w @ async.coroutine dekorator, i czekają na słowa kluczowe, a wydajnością z przyszłości obsługiwać wszystkie z nich są zbliżone do. Skręcone z perspektywy procesu (wielozadaniowość kooperacyjna)). (Aby uzyskać aktualny stan obsługi Twisted dla Pythona 3, sprawdź: https://twistedmatrix.com/documents/current/core/howto/python3.html )
Wersja dystrybuowana :
Kolejną dziedziną przetwarzania, o którą nie pytałeś, ale którą warto rozważyć, jest przetwarzanie rozproszone . Istnieje wiele narzędzi i struktur Pythona do przetwarzania rozproszonego i obliczeń równoległych. Osobiście uważam, że najłatwiejszy w użyciu jest taki, który jest najrzadziej uważany za znajdujący się w tej przestrzeni.
Tworzenie rozproszonego przetwarzania wokół Redis jest prawie trywialne . Cały magazyn kluczy może być używany do przechowywania jednostek pracy i wyników, LISTY Redis mogą być używane jako
Queue()
podobne obiekty, a obsługa PUB / SUB może być używanaEvent
do obsługi podobnej do tej. Możesz haszować swoje klucze i używać wartości replikowanych w luźnym klastrze instancji Redis, aby przechowywać topologię i mapowania znaczników skrótu, aby zapewnić spójne mieszanie i przełączanie awaryjne w celu skalowania poza możliwości pojedynczego wystąpienia w celu koordynowania pracowników i organizowanie danych (piklowane, JSON, BSON lub YAML) wśród nich.Oczywiście, jak zacząć budować większą skalę i bardziej wyrafinowane rozwiązania wokół Redis jesteś ponownego wykonania wiele funkcji, które zostały już rozwiązane za pomocą, Seler , Apache Spark i Hadoop , Zookeeper , etcd , Cassandra i tak dalej. Wszystkie mają moduły umożliwiające dostęp do swoich usług w języku Python.
[Aktualizacja: Kilka zasobów do rozważenia, jeśli rozważasz Python do intensywnego obliczania w systemach rozproszonych: IPython Parallel i PySpark . Chociaż są to rozproszone systemy obliczeniowe ogólnego przeznaczenia, są one szczególnie łatwo dostępnymi i popularnymi podsystemami nauki i analizy danych].
Wniosek
Masz do dyspozycji gamę alternatyw przetwarzania dla Pythona, od jednowątkowych, z prostymi synchronicznymi wywołaniami do podprocesów, pulami odpytywanych podprocesów, wielowątkową i wielowątkową, kooperatywną wielozadaniowością sterowaną zdarzeniami, aż po przetwarzanie rozproszone.
źródło
W podobnym przypadku zdecydowałem się na oddzielne procesy i odrobinę niezbędnej komunikacji przez gniazdo sieciowe. Jest bardzo przenośny i dość prosty do wykonania przy użyciu Pythona, ale prawdopodobnie nie prostszy (w moim przypadku miałem też inne ograniczenie: komunikację z innymi procesami napisanymi w C ++).
W twoim przypadku prawdopodobnie wybrałbym proces wieloprocesowy, ponieważ wątki Pythona, przynajmniej w przypadku korzystania z CPythona, nie są prawdziwymi wątkami. Cóż, są to natywne wątki systemowe, ale moduły C wywoływane z Pythona mogą, ale nie muszą, zwolnić GIL i pozwolić innym wątkom na uruchomienie ich podczas wywoływania kodu blokującego.
źródło
Aby używać wielu procesorów w CPythonie, jedynym wyborem jest
multiprocessing
moduł. CPython blokuje swoje wewnętrzne elementy ( GIL ), co zapobiega równoległej pracy wątków na innych procesorach CPU.multiprocessing
Moduł tworzy nowe procesy (jaksubprocess
) i zarządza komunikacją między nimi.źródło
Wyłuskuj i pozwól unixowi wykonać twoje zadania:
użyj iterpipes, aby zawinąć podproces, a następnie:
Ze strony Teda Ziuby
INPUTS_FROM_YOU | xargs -n1 -0 -P LICZ ./proces #LICZBA równoległych procesów
LUB
Równoległy Gnu również posłuży
Spędzasz czas z GIL, wysyłając chłopaków z zaplecza, aby wykonali twoją pracę wielordzeniową.
źródło