Hejto.pl
Dodaj post

Wpisz coś do wyszukania (minimum 2 znaki)

Wpis użytkownika qarmin w Python

Twórca

w Python

5piorunów

W 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)

Kosmonauta2piorunów

@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
Kosmonauta2piorunów

@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ń.

Twórca0piorunów

@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