De ce a fost scris Python cu GIL?

Blocarea globală a interpretorului (GIL) pare a fi adesea citată ca un motiv major pentru care threading-ul și altele asemenea este dificil în Python – ceea ce ridică întrebarea „De ce s-a făcut asta în primul rând?”

Fiind un programator, nu am nici o idee de ce ar putea fi asta – care a fost logica în spatele GIL?

Comentarii

  • Articolul Wikipedia afirmă că ” GIL poate fi o barieră semnificativă în calea paralelismului – un preț plătit pentru că are dinamismul limbajului ” și continuă spunând că ” Motivele pentru care se folosește o astfel de blocare includ: viteza crescută a programelor cu un singur fir (nu este necesară achiziționarea sau eliberarea blocărilor pe toate structurile de date separat) și integrarea ușoară a bibliotecilor C care sunt de obicei nu este sigur pentru fire. ”
  • @RobertHarvey, Dinamismul nu are nimic de făcut Cu acesta. Problema este mutația.
  • stackoverflow.com/questions/265687/…
  • ‘ nu poate ajuta să simt că, la fel ca Java ‘, lipsa numerelor nesemnate, a fost menită să împiedice persoanele care nu ‘ nu știu ce fac ‘ să tragă singuri în picior. Din păcate, oricine știe știe ce face ‘ face un limbaj deficitar, ceea ce este o adevărată rușine, deoarece Python stâncă în atât de multe moduri
  • @Basic trebuie să existe o modalitate standard de a face față matricelor de octeți în Java (nu l-am folosit ‘ de mult timp) pentru a face cripto matematică. Python (de exemplu) nu are ‘ nu are numere semnate, dar nici nu aș încerca ‘ să încerc să fac operații bitbit cu el, deoarece există moduri mai bune.

Răspuns

Există mai multe implementări ale Python, de exemplu, CPython, IronPython, RPython, etc.

Unele dintre ele au un GIL, altele nu. De exemplu, CPython are GIL:

De la http://en.wikipedia.org/wiki/Global_Interpreter_Lock

Aplicațiile scrise în limbaje de programare cu GIL pot fi proiectate pentru a utiliza procese separate pentru a obține paralelism complet, deoarece fiecare proces are propriul interpret și la rândul său, are propriul GIL.

Avantajele GIL

  • Viteză crescută a programelor cu un singur fir.
  • Integrarea ușoară a bibliotecilor C care, de obicei, nu sunt sigure pentru fire.

De ce Python (CPython și alții) folosește GIL

În CPython, blocarea interpretului global sau GIL, este un mutex care împiedică executarea simultană a mai multor fire native de la Python bytecodes. Această blocare este necesară, în principal, deoarece gestionarea memoriei CPython nu este sigură pentru fire.

GIL este controversat deoarece împiedică programele CPython multithread să profite din plin de sistemele multiprocesor în anumite situații. Rețineți că blocarea sau operațiile de lungă durată, cum ar fi I / O, procesarea imaginilor și restrângerea numărului NumPy, se întâmplă în afara GIL. Prin urmare, numai în programele multithread care petrec mult timp în interiorul GIL, interpretând codul de cod CPython, GIL devine blocaj.

Python are un GIL spre deosebire de blocarea cu granulație fină din mai multe motive:

  • Este mai rapid în cazul cu un singur fir.

  • Este mai rapid în cazul multi-threaded pentru programele legate de i / o.

  • Este mai rapid în cazul multi-threaded pentru programele legate de cpu care fac munca lor intensivă în calculatoare în bibliotecile C.

  • Se realizează Extensii C mai ușor de scris: nu va exista comutator de fire Python, cu excepția cazului în care îi permiteți să se întâmple (adică între macro-urile Py_BEGIN_ALLOW_THREADS și Py_END_ALLOW_THREADS).

  • Face mai ușoară împachetarea bibliotecilor C. Nu trebuie să vă faceți griji cu privire la siguranța firelor. Dacă biblioteca nu este sigură la fir, pur și simplu păstrați GIL blocat în timp ce îl apelați.

GIL poate să fie lansat de extensiile C. Biblioteca standard a lui Python eliberează GIL în jurul fiecărui apel de blocare I / O. Astfel, GIL nu are nicio consecință asupra performanței serverelor legate de i / o. Puteți crea astfel servere de rețea în Python folosind procese (fork), fire sau i / o asincrone, iar GIL nu vă va împiedica.

Bibliotecile numerice din C sau Fortran pot fi apelate în mod similar cu GIL a fost eliberat. În timp ce extensia dvs. C așteaptă finalizarea unui FFT, interpretul va executa alte fire Python.Un GIL este astfel mai ușor și mai rapid decât blocarea cu granulație fină și în acest caz. Aceasta constituie cea mai mare parte a muncii numerice. Extensia NumPy eliberează GIL ori de câte ori este posibil.

Subiectele sunt de obicei o metodă proastă de a scrie majoritatea programelor de server. Dacă sarcina este redusă, bifurcarea este mai ușoară. Dacă încărcarea este mare, i / o asincronă și programarea bazată pe evenimente (de exemplu, folosind cadrul Twisted Python) este mai bună. Singura scuză pentru utilizarea thread-urilor este lipsa os.fork pe Windows.

GIL este o problemă dacă, și numai dacă, lucrați intensiv în CPU în Python pur. Aici puteți obține un design mai curat folosind procese și transmiterea mesajelor (de exemplu, mpi4py). Există, de asemenea, un modul de „procesare” în brânza Python magazin, care oferă proceselor aceeași interfață ca firele (de exemplu, înlocuiți threading-ul. Thread cu processing.Process).

Thread-urile pot fi folosite pentru a menține capacitatea de răspuns a unei interfețe grafice indiferent de GIL. (cf. discuția de mai sus), puteți lăsa firul dvs. să producă un proces și să așteptați să se termine.

Comentarii

  • Sună ca strugurii acri pentru mine. Python nu poate ‘ să facă subiectele în mod corespunzător, astfel încât să explicați motivele pentru care firele sunt inutile sau chiar rele. ” Dacă încărcarea este scăzut, fo rking-ul este mai ușor „, serios? Iar GIL este ” mai rapid ” pentru toate acele cazuri numai dacă insistați să utilizați GC de numărare a referințelor.
  • s/RPython/PyPy/g. @MichaelBorgwardt Oferirea de motive pentru care GIL este un punct important al întrebării, nu ‘ nu? Deși aș fi de acord că o parte din conținutul acestui răspuns (și anume discutarea alternativelor) este deoparte. Și în bine sau în rău, recontarea este acum aproape imposibil de scăpat – este adânc înrădăcinată în întregul API și baza de cod; ‘ este aproape imposibil să scapi de el fără a rescrie jumătate din cod și a sparge tot codul extern.
  • Nu ‘ nu uitați biblioteca multiprocessing – standard de la 2.6. ‘ grupurile de lucrători sunt o abstractizare super-slick pentru unele tipuri simple de paralelism.
  • @alcalde Numai dacă nu ‘ nu știți ce faceți ‘ și / sau nu ‘ nu doriți ca firele dvs. să poată lucra în mod cooperativ / comunica. În caz contrar, este ‘ o durere regală în spate, mai ales având în vedere cheltuielile generale de lansare a unui nou proces pe unele sisteme de operare. Avem servere cu 32 de nuclee, așa că pentru a le utiliza pe deplin în CPython am nevoie de 32 de procese ‘. Că ‘ nu este o ” soluție bună ” este ‘ un hack pentru a rezolva inadecvările CPython ‘.
  • Faptul că există fire de execuție pe alte platforme decât Windows ar trebui să fie o dovadă suficientă că bifurcarea nu este ‘ nu este adecvat în orice situație.

Răspuns

Mai întâi off: Python nu are GIL. Python este un limbaj de programare. Un limbaj de programare este un set de reguli și restricții matematice abstracte. Nu există nimic în Specificația limbajului Python care să spună că trebuie să existe un GIL.

Există multe implementări diferite ale Python. Unele au un GIL, altele nu.

O explicație simplă pentru a avea un GIL este că scrierea unui cod simultan este dificilă. Plasând o blocare uriașă în jurul codului tău, îl forțezi să ruleze întotdeauna în serie. Problema rezolvată!

În CPython, în special, un obiectiv important este de a face mai ușoară extinderea interpretului cu pluginuri scrise în C. Din nou, scrierea codului simultan este dificilă, deci garantând că nu va exista simultan, facilitează scrierea extensiilor pentru interpret. În plus, multe dintre aceste extensii sunt doar împachetări subțiri în jurul bibliotecilor existente, care poate nu au fost scrise având în vedere concurența.

Comentarii

  • Că ‘ este același argument ca Java ‘ e lipsa de tipuri numerice nesemnate – dezvoltatorii cred că toți ceilalți sunt mai stupizi decât sunt …
  • @Basic – credeți sau nu, chiar și atunci când ‘ nu sunteți cu adevărat prost, se dovedește că având un limbaj care face presupuneri simplificatoare care înseamnă că nu ‘ nu te gândești la anumite lucruri pentru a le face să funcționeze este încă un lucru util.CPython este excelent pentru anumite lucruri, inclusiv aplicații simple cu mai multe fire (unde programul este legat de IO, care sunt multe și, prin urmare, GIL nu contează), deoarece deciziile de proiectare care au luat GIL, cea mai bună soluție, facilitează și programarea acelor aplicații, în special faptul că acceptă operații atomice pe colecții .
  • @Jules Da, este ‘ este foarte util până când aveți nevoie de aceste capacități. cpython ‘ s ” preferat ” soluție ” scrie-l într-o altă limbă, cum ar fi c ++ „, atunci înseamnă că pierzi fiecare beneficiu individual Python. Dacă ‘ scrieți jumătate din cod în c ++, atunci de ce să începeți de la Python? Sigur, pentru proiectele mici de API / lipici, ‘ este rapid și ușor, iar pentru ETL ‘ este egal, dar ‘ nu este potrivit pentru nimic care necesită ridicări grele. La fel ca și utilizarea Java pentru a vorbi cu hardware-ul … Este ‘ aproape comic, cercurile prin care trebuie să treci.
  • @Basic One of Python ‘ s și, astfel, pentru a extinde filozofiile fundamentale ale CPython ‘ este de a face tehnologia ” prietenoasă și ușor de utilizat „. Programarea paralelă fără blocare globală nu este aceea. Având în vedere că există multe implementări fără GIL, este logic să furnizați cel puțin o implementare care o are.
  • Spuneți ” este logic să furnizați cel puțin o implementare care o are. ” place ‘ este concluzia evidentă, dar nu există un alt limbaj ‘ sunt conștient de faptul că își dezvoltă dezvoltatorii în acest fel, deci nu poate ‘ să fie atât evident.

Răspuns

Care este scopul unui GIL?

Documentația CAPI are acest lucru de spus cu privire la subiect:

Interpretul Python nu este complet sigur . Pentru a sprijini programele Python cu mai multe fire, există o blocare globală, numită blocare interpretă globală sau GIL, care trebuie ținută de firul curent înainte de a putea accesa în siguranță obiecte Python. Fără blocare, chiar și cele mai simple operații ar putea provoca probleme într-un program cu mai multe fire: de exemplu, atunci când două fire incrementează simultan numărul de referință al aceluiași obiect, numărul de referință ar putea ajunge să fie incrementat o singură dată în loc de două ori. p>

Cu alte cuvinte, GIL previne corupția statului. Programele Python nu ar trebui să producă niciodată o eroare de segmentare, deoarece sunt permise numai operațiuni în siguranță în memorie. GIL extinde această asigurare la programele multi-thread.

Care sunt alternativele?

Dacă scopul GIL este de a proteja statul de corupție, atunci o alternativă evidentă este blocarea unui bob mult mai fin; poate la un nivel per obiect. Problema cu acest lucru este că, deși s-a demonstrat că crește performanța programelor multi-thread, are mai multe programe overhead și, ca urmare, suferă programe single-thread.

Comentarii

  • Ar fi minunat să lăsați un utilizator să ruleze un program cu o opțiune de interpretare care să înlocuiască gil pentru blocarea cu granulație fină și să știe cumva – într-un mod citit – dacă procesul curent a fost ridicat cu sau fără gil.
  • În ciuda GIL, am reușit să produc o eroare de segmentare într-un program multithread din cauza utilizării neglijentă a modulului pyodbc. Astfel, ” nu ar trebui să producă niciodată o eroare de segmentare ” este o eroare.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *