Wpis użytkownika qarmin w Python
qarminTwórca
5piorunówW pracy, udało nam się przepisać celery z Pythona na nasz własny menedżer tasków w Rust.
Wydajność jest zauważalnie lepsza, bo korzystamy ze starych komponentów i tam różnice są o wiele bardziej widoczne, niż na zwykłym komputerze, gdzie oba rozwiązania przetwarzają zadania niemal natychmiast.
Zatem mamy część z kodem w Rust, ale ciągle część z obliczeń musimy wykonać w pojedynczym procesie pythona do którego wysyłamy resty z zapytaniami.
Jednak celery miało jeden duży plus - uruchamiało się jako osobne procesy(co wydłużało o kilka sekund proces uruchamiania programu), dzięki czemu operacje numpy/scipy na CPU, były ładnie rozdzielane po stronie pythona na wszystkie wątki(na urządzeniu są 4 rdzenie i tyle samo procesów celery było uruchamianych)
Operacje obliczania statystyk(mowa o powyższym zadaniu korzystającym z numpy/scipy) są często zlecane w większej ilości na raz, dlatego ważne jest by to zrównoleglić.
Próbowałem robić to przez proste rozdzielanie tasków na wiele wątków, jednak, czasy bywały nawet czasami gorsze, niż jak robiłem to w jednym wątku - domyślam się że to wina GIL i tego że nie działa zbytnio dobrze, gdy wątki wykonują masę rzeczy na cpu.
Drugim problemem jest to że w przypadku równoległych działań wyskakują dziwne ostrzeżenia, przez co myślę że np. matplotlib, nie jest przystosowany do działania z wielu wątków(mimo że zadania które wykonuję od początku do końca działają tylko w jednym wątku), bo przechowuje globalnie jakieś parametry ze swoim stanem.
Kojarzycie, w jaki sposób, mógłbym w miarę prosto, móc wykonywać te obliczenia po stronie pythona na wielu wątkach?
Komentarze (3)
@qarmin jest standardowy moduł w Pythonie, nazywa się multiprocessing. W nim znajdziesz Pool i metody typu starmap i map. Najczęściej z tego korzystam gdy trzeba zrównoległość obliczenia.
https://superfastpython.com/multiprocessing-pool-map/
># SuperFastPython.com
># example of parallel map() with the process pool
>from random import random
>from time import sleep
>from multiprocessing.pool import Pool
># task executed in a worker process
>def task(identifier):
> # generate a value
> value = random()
> # report a message
> print(f'Task {identifier} executing with {value}', flush=True)
> # block for a moment
> sleep(value)
> # return the generated value
> return value
># protect the entry point
>if _name == 'main_':
> # create and configure the process pool
> with Pool() as pool:
> # execute tasks in order
> for result in pool.map(task, range(10)):
> print(f'Got result: {result}', flush=True)
> # process pool is closed automatically
Na zamieszczonym zdjęciu znajdziesz wszystkie metody z klasy Pool które możesz wykorzystać.
Multiprocessing Pool.map() in Python - Super Fast PythonYou can apply a function to each item in an iterable in parallel using the Pool map() method. In this tutorial you will discover how to use a parallel version of map() with the process pool in Python. Let’s get started. Need a Parallel Version of map() The multiprocessing.pool.Pool in Python provides a pool of […]Super Fast Python@qarmin dla tego podałem multiprocessing a nie threading. Przy tym drugim, wątki są blokowane przez GIL, i są one obsługiwany tylko przez jeden rdzeń procesora. Najczęściej wykorzystuje się do zrównoleglenia operacji IO. Tym czasem multiprocessing uruchamia oddzielne procesy pochodne od głównego, dlatego każdy proces może być w tym samym czasie obsłużony przez CPU.
Metoda którą Ci podałem jest o tyle prosta że o nic nie trzeba dbać, a funkcje zwracają wyniki obliczeń.
@markxvyarov Działa to lepiej, niż ręczne tworzenie i zarządzanie wątkami?
Boję się że również tutaj GIL pokaże swoje ograniczenia i w rzeczywistości będzie to wszystko działało, niemal tak jak w jednym wątku