Programování

Vícejádrový Python: Tvrdý, hodný a dosažitelný cíl

U všech skvělých a pohodlných funkcí Pythonu zůstává jeden cíl mimo dosah: Aplikace Pythonu spuštěné na referenčním tlumočníkovi CPython a paralelní používání více jader CPU.

Toto je již dlouho jedním z největších kamenů úrazu Pythonu, zejména proto, že všechna řešení jsou neohrabaná. Naléhavost najít dlouhodobé řešení problému roste, zejména proto, že se stále zvyšuje počet procesorů (viz 24jádrový gigant Intel).

Jeden zámek pro všechny

Ve skutečnosti je možné použít vlákna v aplikacích Pythonu - spousta z nich již ano. Co jene je možné, aby CPython spouštěl vícevláknové aplikace s každým spuštěním vlákna paralelně na jiném jádru. Správa interní paměti CPython není bezpečná pro vlákna, takže tlumočník spouští pouze jedno vlákno najednou, přepíná mezi nimi podle potřeby a kontroluje přístup do globálního stavu.

Tento uzamykací mechanismus, Global Interpreter Lock (GIL), je jediným největším důvodem, proč CPython nemůže spouštět vlákna paralelně. Existuje několik polehčujících faktorů; například I / O operace, jako je čtení disku nebo sítě, nejsou vázány GIL, takže ty mohou volně běžet ve svých vlastních vláknech. Ale cokoli s více vlákny i s CPU je problém.

Pro programátory v Pythonu to znamená, že těžké výpočetní úlohy, které těží z toho, že jsou rozloženy na více jader, nefungují dobře, což znemožňuje použití externí knihovny. Pohodlí práce v Pythonu je spojeno s velkými náklady na výkon, které je těžší spolknout, protože do popředí se dostávají rychlejší a stejně pohodlné jazyky, jako je Google Go.

Vyberte zámek

Postupem času se objevila spousta možností, které zlepšují - ale nevylučují - limity GIL. Jednou standardní taktikou je spuštění více instancí CPythonu a sdílení kontextu a stavu mezi nimi; každá instance běží nezávisle na druhé v samostatném procesu. Ale jak vysvětluje Jeff Knupp, zisky poskytované paralelním spuštěním mohou být ztraceny úsilím potřebným ke sdílení stavu, takže tato technika je nejvhodnější pro dlouhodobé operace, které spojují své výsledky v průběhu času.

Rozšíření C nejsou vázána GIL, takže mnoho knihoven pro Python, které potřebují rychlost (například knihovna matematiky a statistik Numpy), může běžet na více jádrech. Ale omezení v samotném CPythonu zůstávají. Pokud je nejlepším způsobem, jak se vyhnout GIL, je použití C, bude to pohánět více programátorů od Pythonu a směrem k C.

PyPy, verze Pythonu, která kompiluje kód pomocí JIT, se nezbaví GIL, ale vynahradí to jednoduše tím, že kód běží rychleji. V některých ohledech to není špatná náhrada: Pokud je rychlost hlavním důvodem, proč jste hleděli na vícevláknové zpracování, může být PyPy schopen poskytnout rychlost bez komplikací vícevláknového zpracování.

Nakonec byl samotný GIL v Pythonu 3 trochu přepracován, s lepším obslužným programem pro přepínání vláken. Ale všechny jeho základní předpoklady - a omezení - zůstávají. Stále existuje GIL a stále drží řízení.

Žádný GIL? Žádný problém

Navzdory tomu všemu pokračuje hledání Pythonu bez GIL kompatibilního s existujícími aplikacemi. Jiné implementace Pythonu úplně odstranily GIL, ale za cenu. Například Jython běží nad JVM a místo GIL používá systém sledování objektů JVM. IronPython používá stejný přístup prostřednictvím CLR společnosti Microsoft. Ale oba trpí nekonzistentním výkonem a někdy běží mnohem pomaleji než CPython. Také nemohou snadno komunikovat s externím kódem C, takže mnoho existujících aplikací v Pythonu nebude fungovat.

PyParallel, projekt vytvořený Trentem Nelsonem z Continuum Analytics, je „experimentální vidlice typu proof-of-concept pro Python 3 navržená k optimálnímu využití více jader CPU.“ Neodstraňuje GIL, ale zmírňuje jeho dopad nahrazením asynchronní modul, takže aplikace, které používajíasynchronní pro paralelismus (jako je vícevláknový I / O jako webový server) má největší užitek. Projekt již několik měsíců spí, ale jeho dokumentace uvádí, že jeho vývojářům vyhovuje, když si udělají čas, aby to napravili, takže jej lze nakonec zahrnout do CPythonu: „Není nic špatného na pomalém a stabilním postupu, pokud míříte správným směrem. “

Jedním z dlouhodobých projektů tvůrců PyPy byla verze Pythonu, která používá techniku ​​zvanou „softwarová transakční paměť“ (PyPy-STM). Výhodou podle tvůrců PyPy je „můžete provést drobné úpravy svých stávajících, ne vícevláknových programů a přimět je, aby používaly více jader.“

PyPy-STM zní jako magie, ale má dvě nevýhody. Zaprvé je to nedokončená práce, která aktuálně podporuje pouze Python 2.xa zadruhé, stále to vyžaduje výkonnostní hit pro aplikace běžící na jednom jádru. Jelikož jednou z podmínek citovaných tvůrcem Pythonu Guidem van Rossumem pro jakékoli pokusy o odstranění GIL z CPythonu je, že jeho nahrazení by nemělo snižovat výkon u jednojádrových aplikací s jedním vláknem, taková oprava v CPythonu nepřistane v současném stavu.

Pospěšte si a počkejte

Larry Hastings, hlavní vývojář Pythonu, na PyConu 2016 sdílel některé ze svých názorů na to, jak lze odstranit GIL. Hastings zdokumentoval své pokusy o odstranění GIL, a tím skončil s verzí Pythonu, který neměl GIL, ale běžel agonizačně pomalu kvůli neustálým chybám v mezipaměti.

Můžete přijít o GIL, shrnul Hastings, ale musíte mít nějaký způsob, jak zaručit, že globální objekty bude upravovat pouze jedno vlákno najednou - například tak, že takové změny stavu zvládne vyhrazené vlákno v tlumočníkovi.

Jednou z dlouhodobých dobrých zpráv je, že pokud CPython zbaví GIL, vývojáři používající tento jazyk již budou připraveni využívat multithreading. Mnoho změn se nyní promítlo do syntaxe Pythonu, například fronty a asynchronní/čekat klíčová slova pro Python 3.5, usnadněte rozdělení úkolů mezi jádra na vysoké úrovni.

Množství práce potřebné k tomu, aby Python GIL-less byl až na všechny, ale zaručuje, že se nejprve zobrazí v samostatné implementaci, jako je PyPy-STM. Ti, kteří chtějí vyzkoušet systém bez GIL, mohou tak učinit prostřednictvím takového úsilí třetí strany, ale původní CPython prozatím pravděpodobně zůstane nedotčený. Doufáme, že čekání nebude o moc delší.