ĘZYK PYTHON DLA KAŻDEGO NAUKOWCAftp.us4us.eu/.../Python_dla_naukowca2-HPC.pdf · Python GIL •...
Transcript of ĘZYK PYTHON DLA KAŻDEGO NAUKOWCAftp.us4us.eu/.../Python_dla_naukowca2-HPC.pdf · Python GIL •...
JĘZYK PYTHON - NARZĘDZIE DLA KAŻDEGO NAUKOWCA
Marcin Lewandowski [ [email protected] ]
HPC
2
Co zrównoleglać (logicznie i fizycznie)?
• Programowanie z asynchronicznymi zdarzeniami – np. interfejs użytkownika, komunikacja sieciowa, obsługa operacji I/O (pliki), etc.
• Programy których wydajność można zwiększyć przez ukrycie latencji (opóźnień): – Długo trwające operacje I/O (zapis/odczyt z plików, komunikacja
sieciowa) – W czasie trwania tych operacji program może wykonać inną
potrzebną pracę • Programy intensywnie obliczeniowe:
– Zagadnienia numeryczne, które silnie wykorzystują jednostki obliczeniowe mogą uzyskać znacznie przyśpieszenie przy wykonywaniu na komputerze z wieloma procesorami/rdzeniami jeśli zadani zostanie podzielone na wątki (lub procesy)
3
Równoległość zadań
4 Źródło: http://en.wikipedia.org/wiki/OpenMP
Co to jest proces?
• Niezależny kontekst wykonawczy wraz z własną odizolowaną przestrzenią pamięci i zasobami
• Procesy pomiędzy sobą nie dzielą niczego (z wyjątkiem maszyny ;-)
• Procesy muszą komunikować się między sobą za pomocą mechanizmów IPC (Inter-Process Communication); do wymiany danych oraz synchronizacji
• Procesy są „ciężkie” (wymagają sporo zasobów)
5
Co to jest wątek?
• Kontekst wykonywania programu • Współdzieli pamięć i stan ze swoim rodzicem
(procesem w którym żyje). • Wątki są „lekkie” (tj. nie wymagają dużych zasobów) • Posiadają własny stos (i prywatne zmienne lokalne) • Nie muszą używać IPC do komunikacji z innymi
wątkami w procesie • Wątki Python = wątki systemu operacyjnego (w Linux
POSIX “Threads” – pthreads)
6
Wątek vs. Proces
• Wątki współdzielą wszystko, dlatego programista musi pilnować wielodostępu do wspólnych danych
• Procesy nie współdzielą nic, dlatego programista musi explicite zatroszczyć się o współdzielenie danych/stanu pomiędzy procesy
7
Python GIL
• Python GIL (Global Interpreter Lock) – zabezpiecza interpreter Pythona przed wielodostępem, co niestety ogranicza możliwość fizycznego zrównoleglenia.
• Co robi GIL: – GIL zapewnia, że tylko jeden wątek jest wykonywany w
danej chwili przez interpreter Pythona (cPythonVM) – GIL kontroluje sposób wykonywania wątków. Python ustala
jak długo wątek ma być wykonywany (~10 instrukcji). – Python wykorzystuje wątki systemu operacyjnego, ale sam
kontroluje przekazywanie kontekstu wykonawczego.
8
GIL
• Co dobrego daje GIL: – Realizuje koncepcję kooperatywnej wielozadaniowości – Interpreter wie najlepiej kiedy bezpiecznie przełączyć
kontekst wykonywania – Często jest bardzie efektywny od wielozadaniowości z
wywłaszczaniem – Może być ‚zwolniony’ w module C (np. dla
długotrwałych operacji I/O) – Łatwe kodowanie (nie musimy się pilnować) – Łatwą implementację modułów rozszerzeń
9
Moduły Moduł Funkcje
thread _thread (w Python 3+)
Nisko poziomowy moduł do wielowątkowości
threading Wysoko poziomowy moduł do wielowątkowości (zbudowany na bazie modułu thread)
multiprocessing Wysoko poziomowy moduł do wieloprocesowości
subprocess Zarządzanie podprocesami
select Waiting for I/O completion
10
Moduł threading
• Wysoko poziomowy moduł do realizacji wielowątkowości
• Zbudowany na nisko poziomowym module thread
• Działa w oparciu o wątki systemu operacyjnego
• Równoległość jest ograniczona przez GIL
11
Obiekt threading.Thread • class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}) – konstruktor
z argumentami nazwanymi: – group – powinno być None; zarezerwowane dla przyszłych rozszerzeń (ThreadGroup). – target – wołalny obiekt, który ma być uruchomiony w nowym wątku przez metodę run() – name – nazwa wątku. Domyślnie tworzona w konstruktorze w postaci “Thread-N”, gdzie N jest liczbą. – args – is the argument tuple for the target invocation. Defaults to (). – kwargs – is a dictionary of keyword arguments for the target invocation. Defaults to {}.
• Jeśli podklasa nadpisuje konstruktor musi wywołać na samym początku metodę klasy bazowej Thread.__init__().
• Thread.start() – startuje wątek. Musi być wywołana tylko raz dla obiektu (zgłasza wyjątek RuntimeException jeśli wywołana więcej razy). Startuje wątek, w którym będzie wywołana metoda run().
• Thread.run() – metoda realizowana w wątku, która może być przedefiniowana w klasie potomnej. Metoda w klasie bazowej wywołuje obiekt przekazany w konstruktorze (target) z argumentami args i kwargs.
• Thread.join([timeout]) – czeka na zakończenie wątku. Jest to metoda blokująca z zadanym czasem timeout (float w sekundach). W celu określenia stanu wątku po timeout należy użyć metod: isAlive(). Metoda join() może być wywoływana wiele razy dla jednego wątku.
12
Obiekt threading.Lock • Prymitywny obiekt synchronizacyjny typu lock; nie związany z
konkretnym wątkiem. • Lock ma dwa stany „locked” i „unlocked”. Po utworzeniu jest w
stanie „unlocked”. • Lock.acquire() – Gdy w stanie „unlocked” zmienia natychmiast stan
na ”locked”; Gdy w stanie „locked”, acquire() blokuje do momenty wywołania release() w innym wątku i ustawia do stanu „locked”.
• Lock.release() – powinna być wołana tylko w stanie „locked” w celu natychmiastowej zmiany stanu na „unlocked”. Wyjątek RuntimeError jest generowany gdy zwalniany jest obiekt w stanie „unlocked”. Gdy więcej wątków jest zablokowanych na acquire(), tylko jeden z nich jest odblokowywany dla jednego wywołania release().
• Wszystkie te metody są wykonywane atomowo.
13
Obiekt threading.Semaphore • Jeden z najstarszy obiektów synchronizacyjnych (wymyślony przez W.
Dijkstra). • Semafor zarządza wewnętrznym licznikiem, który jest zmniejszany przy
każdym wywołaniu acquire() i zwiększany przy każdym wywołaniu release(). Licznik jest zawsze >=0
• Funkcja acquire() wołana dla Semafora o wartości 0 blokuje się do momentu kiedy inny wątek wywoła release().
• class threading.Semaphore([value]) – tworzy semafor; opcjonalny argument value określa początkową wartość licznika (domyślnie 1).
• Semaphore.acquire([blocking]) – pobiera semafor. – Wywołany bez argumentów (lub blocking=Ture): jeśli licznik >0 zmniejsza jego
wartość o 1 i wraca natychmiast, jeśli licznik=0 blokuje do do momentu wywołania release() w innym wątku.
– Wywołany z blocking=False: działa w trybie nie blokującym zwracając False natychmiast gdy nie może pobrać semafora lub True gdy udało się go pobrać.
• Semaphore.release() – zwalnia semafor; zwiększając stan licznika o 1.
14
Moduł multiprocessing • Interfejs „wielowątkowy” opartym na procesach • Składnia analogiczna do modułu threading:
– threading : • Thread(target=do_work, args=(work_queue,))
– multiprocessing: • Process(target=do_work, args=(work_queue,))
• TRZEBA PAMIĘTAĆ!
– Procesy nie współdzielą NICZEGO (w szczególności pamięci)! – Wymiana danych pomiędzy procesami może odbywać się przez
jeden z mechanizmów komunikować IPC (Inter-Process Communication)
15
Wątki Python/C
• Python może tworzyć wątki w modułach C/C++: – Wątki C/C++ są niezależnymi wątkami natywnymi
(nie Pythonowymi) i potencjalnie mogą działać równolegle (o ile zwolnimy GIL).
– W modułach C można programowo zwolnić GIL
• Inną alternatywą jest utworzenie wielu interpreterów Pythona (w jednym lub wielu procesach)
16
Python IPC & Networking • Następujące moduły zapewniają mechanizmy komunikacji IPC:
– subprocess – zarządzanie procesami potomnymi + mechanizm strumieni I/O (popen)
– socket – nisko poziomowy interfejs sieciowy – ssl – warstwa SSL dla socket – signal – obsługa sygnałów dla zdarzeń asynchronicznych – asyncore – asynchroniczne socket – asynchat – asynchroniczne socket + komenda/odpowiedź
• signal, subprocess – działają tylko dla dwóch procesów na tej samej maszynie
• inne moduły komunikacji sieciowej mogą służyć do komunikacji wielu procesów na jednej lub wielu maszynach w sieci.
17
Sumowanie liczb pierwszych 3 implementacje
• Suma liczb pierwszych w zakresie 1-10000 – Jednowątkowa – 152 sek. – Wielowątkowa – 425 sek. ! – Wieloprocesowa – xxx sek.
18 Na podstawie: Jesse Noller, Getting started with Concurrency ...using Multiprocessing and Threading, PyWorks, Atlanta 2008
EX: hpc
Sumowanie liczb pierwszych 3 implementacje
19
MT 4 wątki
Poważne HPC
• MPI – Message Passing Interface – dla systemów z pamięcią rozproszoną (http://www.mcs.anl.gov/research/projects/mpi)
• OpenMP – dla systemów z pamięcią współdzieloną (http://openmp.org)
20
MPI w Python – mpi4py
• MPI jest de facto standardem dla systemów z typu message-passing.
• MPI jest zestawem API (Application Programmer Interfaces) wywoływalnych przez programy użytkownika w C/C++ (i innych językach)
• Istnieje wiele implementacji MPI – zarówno open source (generyczne) oraz zamknięte i zoptymalizowane do konkretnych maszyn.
21
Implementacje MPI dla Pythona
• mpi4py (http://mpi4py.scipy.org) – API bazuje na bibliotece MPI-2 C++ – Działa z open-source implementacjami MPI
(MPICH2, Open MPI, MPICH1, LAM) – mpi4py pozwala na użycie z Pythona kodu
C/C++/Fortran opartego na MPI (za pomocą: Cython, SWIG, F2Py)
• pympi (http://pympi.sourceforge.net) – Starsza implementacja
22 Patrz: http://fperez.org/py4science/2009_siam_cse/mpi4py_ldalcin_bgranger_siam_cse09
GPGPU
• PyCUDA – Zapewnia na pełny dostęp do Nvidia CUDA API z
Pythona – Many abstractions are already provided for you
• PyOpenCL – j.w. ale przez interfejs OpenCL
23
Różne takie inne opcje
• IPython for parallel computing • Bulk Synchronous Parallel (BSP) model sequence of
super steps • Twisted – reactor based architectures • MPI (pyMPI, Pypar, MPI for Python, pypvm) • Pyro (distributed object system) • Linda (PyLinda) • Scientific Python (master/slave computing model) data
distribution through call parameters/replication • …
24
Lektura uzupełniająca
• Norman Matloff, Programming on Parallel Machines, http://heather.cs.ucdavis.edu/˜matloff/ParProcBook.pdf
• Maurice Herlihy, Nir Shavit, The Art of Multiprocessor Programming, Morgan Kaufmann,2008
• Rohit Chandra, Ramesh Menon, Leo Dagum, David Kohr, Dror Maydan, Jeff McDonald, Parallel Programming in OpenMP, Morgan Kaufmann, 2000
• Barbara Chapman, Gabriele Jost, Ruud van der Pas, Using OpenMP: Portable Shared Memory Parallel Programming, MIT Press, 2007
25
PYTHON + FORTRAN
26
f2py • f2py pozwala na automatyczne tworzenie modułów rozszerzeń PYTHON z kodu Fortran 77/90/95:
– F2py automatycznie generuje funkcje obudowujące (wrappers) dla Pythona z kodu Fortran 77/90/95 – Alternatywnie można określić sposób obudowywania procedur w pliku definicji interfejsu.
• Przykładowa procedura dodająca dwa wektory A i B (pamięć dla wszystkich tablic zapewniona przez procedurę wywołującą): C *** Plik add.f *** SUBROUTINE ZADD(A,B,C,N) C DOUBLE COMPLEX A(*) DOUBLE COMPLEX B(*) DOUBLE COMPLEX C(*) INTEGER N DO 20 J = 1, N C(J) = A(J)+B(J) 20 CONTINUE END
• Wywołanie f2py:
> f2py -m add add.f
• Co powinno wygenerować moduł o nazwie: addmodule.c, który można następnie skompilować jak każdy inny moduł C do Pythona.
27
Co potrafi f2py • Tworzy pliki sygnatur dla kodu/funkcji Fortran (pliki .pyf); sygnatury
zawierają wszystkie informacje o funkcji (nazwa, argumenty i ich typy), które są konieczne do utworzenia funkcji opakowującej (wrapper) w C
• Pliku sygnatur można utworzyć „ręcznie” lub może być zmodyfikowany po wygenerowaniu
• Sygnatury mogą być modyfikowane w celu zmiany sposobu przetwarzania argumentów przekazywanych Python->C->Fortran: – F2PY śledzi zależności pomiędzy argumentami (sprawa kolejności inicjalizacji) – Argumenty mogą być opcjonalne lub niewidoczne, co ułatwia wołanie funkcji z
Pythona – Generalnie można dowolnie zmienić sygnaturę funkcji Fortran (np. zmiana
argumentów ich kolejności, typów). Argumenty będą przetworzone zanim trafią do rzeczywistej funkcji Fortran
• F2PY automatycznie generuje dokumentację __doc__ (opcjonalnie też LaTeX) dla tworzonych rozszerzeń
28
F2py – kompilacja modułu • f2py potrafi także kompilować moduł Fortran i moduł
rozszerzeń: > f2py -c -m add add.f
• Skomplilowany moduł jest już dostępny Python: >>> import add >>> print add.zadd.__doc__ zadd - Function signature: zadd(a,b,c,n) Required arguments: a : input rank-1 array(’D’) with bounds (*) b : input rank-1 array(’D’) with bounds (*) c : input rank-1 array(’D’) with bounds (*) n : input int
29 Patrz dokumentacja: „NumPy User Guide”
f2py używany z Pythona
• f2py jest napisany w Python i może być z niego bezpośrednio używany:
• Przykład – otwórz, skompiluj i użyj : import numpy.f2py as f2py fid = open(’add.f’) source = fid.read() fid.close() f2py.compile(source, modulename=’add’) import add # use it!!!
30
Materiały dodatkowe
• F2py example http://websrv.cs.umt.edu/isis/index.php/F2py_example
• Wrapping Fortran libraries http://www.eurotcl.org/2010/Download/EuroTcl2010-Article-Wrapping_fortran_ArjenMarkus.pdf
• Interfacing Python with Fortran www-uxsup.csx.cam.ac.uk/courses/PythonFortran/f2py1.pdf www-uxsup.csx.cam.ac.uk/courses/PythonFortran/f2py2.pdf
• Wrapping Fortran Code for Python with F2PY mentat.za.net/ctpug-numpy/nmarais-f2pytalk.pdf
31