> paralelně poběží většinou vstupně-výstupní operace, resp. v tuto chvíli dojde k přepnutí vláken
Podle mě GIL nevadí ani při výpočtech. Výpočetní knihovny (jako numpy, opencv…) by ho měly uvolňovat, a pokud to děláte jako já a výpočetní rutiny voláte přes CFFI, tak tam se taky uvolňuje. Numba GIL taky uvolňuje.
Takže si myslím, že pokud vám GIL vadí, tak to znamená, že děláte v Pythoním bytecodu něco, k čemu není určen, a měli byste se zaměřit na odstranění především tohoto problému -- ať už zakompilováním pomocí Numby nebo přepsáním do nativní knihovny -- a ne se to snažit záplatovat sežráním více procesorů.
Pokud se přesto rozhodnete prasit s multiprocessingem, nejspíš budete potřebovat vyřešit management workerů - třeba mám 1000 jobů, ale potřebuju, aby jich současně běželo 6 (počet jader mého procesoru). Na to je Pool, který má funkci map, ale já nedokážu tyhle konstrukty snadno vymyslet, takže jsem si našel, že pro nás lamery je tu apply_async. Používá se to takhle:
from multiprocessing import Pool pool = Pool(processes=6) for i in ...: pool.apply_async(moje_funkce, (její,parametry)) while pool._cache: print("number of jobs pending: ", len(pool._cache)) time.sleep(1) pool.close() pool.join()
Presne tak, ale je potreba ten GIL explicitne uvolnit (z Ccka).
Popr. se da si GIL obcas zamknout (s tim, ze to muze v dane chvili blokovat, protoze ho nejspis drzi nekdo jiny), sahnout na PyObject a zase ho odemknout a pokracovat v C-ckovem kodu.
Hezky je to videt napr. ze zdanlive nesouvisejiciho tutorialu k Cythonu -- kde diky doplnku pro Jupyter notebook jsou zlute podbarvene radky kde je potreba GIL a bile jsou ty kde neni (protoze Cython zvladl pro dany kus kodu vyrobit C-only implementaci): https://cython.readthedocs.io/en/latest/src/tutorial/cython_tutorial.html
Jo, takže nějaký míchání s JSONy stejně nechám v Pythonu, protože v C bychom se z toho zbláznili (a zrovna toto Python umí dobře). Osobně mi tedy přijde nejlepší škálovat procesy a ne vlákna - těch pár mega paměti za VM klidně oželím (není to žádnej moloch) a navíc je start více než ucházející. Zase třeba v porovnání s klasickým JVM, kde to takto dělat asi nehrozí.
multiprocessing.Pool nedoporučuju na nic co by mělo běžet spolehlivě delší dobu nebo neinteraktivně. Problém je v chybějícím robustním řešení chyb, takže volající nemá kontrolu nad poolem a pool je náchylný na zamrznutí (stačí si projít python bugy). Na drobnosti ve skriptu to ale stačí.
22. 2. 2022, 10:45 editováno autorem komentáře
Zdroj jsou bugy python projektu. Chvíli jsem se pokoušel tuhle knihovnu používat na paralelizaci úloh a zasekávalo se to několikrát do týdne (pouštělo se tak asi 50 úloh denně). V dokumentaci se to nepíše, protože to jsou bugy a ne dokumentované chování.
Např.
https://bugs.python.org/issue44462
https://bugs.python.org/issue43805
https://bugs.python.org/issue45021
Podobných bugů je tam spousta a s každou verzí pár ubyde i přibyde.
Neni. Predani rizeni mezi thready se deje na hranici bytecode. Jakmile operace ktera ve zdrojaku vypada jako jedna potrebuje vice bytecodes, neni atomicka z pohledu thread safety. += nebo nektere operace s dictem nejsou safe. Situace je ale neprehlednejsi kvuli prubeznym nahradam nekterych neatomickych operaci atomickymi a nedavnym optimalizacim pomoci vectorcall, kdy nekterr operace zacaly byt safe. B-)
Pekne je to popsano napr. na https://opensource.com/article/17/4/grok-gil
a autoritativni zdroj: https://docs.python.org/3/faq/library.html?highlight=atomic#what-kinds-of-global-value-mutation-are-thread-safe
Zatím jsem vždycky na svoje skriptíky používal multiprocessing. Napadá vás nějaký typ kódu, kde se vysloveně vyplatí použít raději threading?
Přijdou mi dost podobné, multiprocessing mi poskytuje lepší paralelizaci a jeho větší systémové nároky a pomalejší spouštění procesů mi nepřišlo jako nic závažného. Ale nevím, třeba je to u nějakých aplikací zásadní problém.
Dík za názor.