> 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()
Jinýmí slovy GIL nevadí, když NEpracujete s Pythonními objekty že? Tedy přes CFFI se volá nějaká nativní věc, která má svoje data atd.
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í.
Ne lepší, ale spíše jednoduší, ne? Tak máš tam další instanci GC. Pokud potřebuješ udělat 2 procesy, ok, pokud jich ale potřebuješ udělat 100, již je rozdíl kardinální.
Od toho už je jen krůček k mikroslužbám :) Podobnou úvahou se IMHO došlo právě k architektuře rozsekané na nezávislé služby. Akorát se k tomu pak přidaly kontejnery pro správu těch procesů, dále Kubernetes pro správu kontejnerů a teď se řeší, jak si usnadnit správu Kubernetes :)
Jo no. Pokud ale neni nutne skalovat pres vic (virtualnich) stroju, tak by mohly bezne procesy + mozna nejaka magie ohledne SystemD stacit.
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?
v dokumentaci k multiprocessing.Pool se o zadnych potencialnich problemech nepise, jen je tam upozorneni na nutnost zavolani close()
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
Jste mě doběhl. O tomto jsem vůbec nevěděl, budu vést v patrnosti. Stejnak nerozumim, jak může být ""i = i+1" nebezpečné. Buď zachytím i před změnou, nebo po změně. Jaký karambol tady hrozí?
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.
Na IO veci multiprocessing nema smysl. Vlakna navic mohou sahat na globalni promene, procesy nemohou. Thread pool se pouziva i v kombinaci s async await, blokujici kod spoustite pomoci run_in_executor. Je tak implementovana velka cast asyncio knihoven.