Višejezgreni Python: tvrd, dostojan i dostižan cilj

Za sve sjajne i prikladne značajke Pythona jedan je cilj nedostižan: aplikacije Python koje se izvode na referentnom tumaču CPython i paralelno koriste više CPU jezgri.

Ovo je već dugo jedan od najvećih Pythonovih kamena spoticanja, pogotovo jer su sva zaobilazna rješenja nespretna. Hitnost za pronalaženjem dugoročnog rješenja problema raste, posebno kako se računanje jezgri na procesore nastavlja povećavati (vidi Intelov 24-jezgreni behemoth).

Jedna brava za sve

U istini, moguće je koristiti niti u Python aplikacijama - dosta ih to već čini. Ono što  nije moguće je da CPython izvodi višenitne aplikacije sa svakom niti koja se paralelno izvršava na drugoj jezgri. CPythonovo upravljanje unutarnjom memorijom nije sigurno, tako da tumač istodobno izvodi samo jednu nit, prebacujući se između njih po potrebi i kontrolirajući pristup globalnom stanju.

Ovaj mehanizam zaključavanja, Global Interpreter Lock (GIL), najveći je jedini razlog zašto CPython ne može paralelno pokretati niti. Postoje neki olakotni faktori; na primjer, I / O operacije poput čitanja s diska ili mreže nisu ograničene GIL-om, tako da one mogu slobodno raditi u vlastitim nitima. Ali sve što je višenitno i vezano uz CPU predstavlja problem.

Za programere Pythona to znači da teški računski zadaci kojima koristi širenje na više jezgri ne rade dobro, zabranjujući upotrebu vanjske knjižnice. Pogodnost rada u Pythonu ima veliku cijenu performansi, koju je sve teže progutati jer brži, jednako prikladni jezici poput Googleova Go dolaze do izražaja.

Izaberite bravu

Vremenom se pojavila mnoštvo opcija koje poboljšavaju - ali ne uklanjaju - ograničenja GIL-a. Jedna od standardnih taktika je pokretanje više primjeraka CPythona i međusobno dijeljenje konteksta i stanja; svaka se instanca izvodi neovisno o drugoj u zasebnom procesu. No, kako objašnjava Jeff Knupp, dobici koje pruža paralelno trčanje mogu se izgubiti naporima potrebnim za dijeljenje stanja, pa je ova tehnika najprikladnija za dugotrajne operacije koje objedinjuju njihove rezultate tijekom vremena.

C proširenja nisu ograničena GIL-om, tako da se mnoge knjižnice za Python kojima je potrebna brzina (poput matematičke i statističke biblioteke Numpy) mogu pokretati na više jezgri. Ali ograničenja u samom CPythonu ostaju. Ako je najbolji način za izbjegavanje GIL-a ako se koristi C, to će više programera odvesti od Pythona i prema C.

PyPy, inačica Pythona koja kompajlira kod putem JIT-a, ne rješava se GIL-a, ali to nadoknađuje jednostavnim bržim izvršavanjem koda. Na neki način ovo nije loša zamjena: ako je brzina glavni razlog zašto promatrate multithreading, PyPy će možda moći pružiti brzinu bez komplikacija multithreadinga.

Konačno, sam GIL donekle je prerađen u Pythonu 3, s boljim rukovačem za prebacivanje niti. Ali sve temeljne pretpostavke - i ograničenja - ostaju. Još uvijek postoji GIL i još uvijek traje postupak.

Nema GIL-a? Nema problema

Unatoč svemu tome, potraga za Pythonom bez GIL-a, kompatibilnim s postojećim aplikacijama, nastavlja se. Druge implementacije Pythona u potpunosti su uklonile GIL, ali uz cijenu. Na primjer, Jython se izvodi na JVM-u i koristi JVM-ov sustav za praćenje objekata umjesto GIL-a. IronPython zauzima isti pristup putem Microsoftovog CLR-a. Ali obojica pate od nedosljednih performansi i ponekad rade puno sporije od CPythona. Također se ne mogu lako povezati s vanjskim C kodom, tako da mnoge postojeće Python aplikacije neće raditi.

PyParallel, projekt koji je kreirao Trent Nelson iz tvrtke Continuum Analytics, "eksperimentalna je, konceptualna vilica Pythona 3 dizajnirana za optimalno iskorištavanje više procesorskih jezgri." Ne uklanja GIL, ali poboljšava njegov učinak zamjenom asyncmodula, tako da aplikacije koje se koriste  asyncza paralelizam (poput multithreaded I / O poput web poslužitelja) imaju najviše koristi. Projekt miruje nekoliko mjeseci, ali njegova dokumentacija navodi da njegovi programeri mogu odvojiti vrijeme kako bi ga ispravili, tako da se na kraju može uključiti u CPython: "Nema ništa loše u polaganom i stabilnom sve dok idete u pravom smjeru ".

Jedan dugogodišnji projekt stvaratelja PyPy-a bila je inačica Pythona koja koristi tehniku ​​koja se naziva "softverska transakcijska memorija" (PyPy-STM). Prednost je, prema PyPyjevim kreatorima, u tome što "možete napraviti manja dorađivanja postojećih programa koji ne sadrže više nitka i natjerati ih da koriste više jezgri."

PyPy-STM zvuči magično, ali ima dva nedostatka. Prvo, to je posao u tijeku koji trenutno podržava samo Python 2.x, a drugo, još uvijek je potreban učinak za programe koji rade na jednoj jezgri. Budući da je jedna od odredbi koje je Pythonov tvorac Guido van Rossum citirao za bilo koji pokušaj uklanjanja GIL-a iz CPythona da njegova zamjena ne bi trebala pogoršati performanse jednostrukih jednostrukih aplikacija, ovakav popravak neće doći u CPython u sadašnjem stanju.

Požurite i pričekajte

Larry Hastings, jezgroviti programer za Python, podijelio je neka od svojih stavova na PyConu 2016 o tome kako se GIL može ukloniti. Hastings je dokumentirao svoje pokušaje uklanjanja GIL-a i pritom je završio s verzijom Pythona koja nije imala GIL, ali je trpjela mučno polako zbog stalnih promašaja u predmemoriji.

Možete izgubiti GIL, sažeo je Hastings, ali morate imati neki način da zajamčite da samo jedna nit istodobno mijenja globalne objekte - na primjer, tako da posebna interpretacija obrađuje takve promjene stanja.

Dugoročna dobra vijest je da će, ako i kada CPython izbaci GIL, programeri koji koriste jezik već biti pripremljeni za korištenje multithreadinga. Mnoge promjene koje su sada uvrštene u Pythonovu sintaksu, poput redova i async/ awaitključnih riječi za Python 3.5, olakšavaju raspodjelu zadataka između jezgri na visokoj razini.

Ipak, količina posla koja je potrebna da bi Python bio bez GIL-a, ali jamči da će se prvo prikazati u zasebnoj implementaciji poput PyPy-STM. Oni koji žele isprobati sustav bez GIL-a to mogu učiniti zahvaljujući naporima treće strane, ali izvorni CPython vjerojatno će zasad ostati netaknut. Evo nadajući se da čekanje nije puno duže.