Domanda

Che cos'è un blocco interprete globale e perché è un problema?

Un sacco di rumore è stato fatto intorno rimuovere il GIL da Python, e mi piacerebbe capire il motivo per cui è così importante. Non ho mai scritto un compilatore né un interprete me stesso, in modo da non essere frugale con i dettagli, io probabilmente bisogno di loro per capire.

È stato utile?

Soluzione

GIL di Python ha lo scopo di serializzare l'accesso alle parti interne interprete di diversi thread. Nei sistemi multi-core, significa che più thread non possono fare uso efficace del core multipli. (Se il GIL non ha portato a questo problema, la maggior parte delle persone non sarebbe preoccuparsi della GIL - è solo di essere cresciuto come un problema a causa della crescente prevalenza di sistemi multi-core.) Se si vuole capire in dettaglio, è possibile visualizzare questo video o guardare questa serie di diapositive . Potrebbe essere troppe informazioni, ma poi ha chiedere per i dettagli: -)

Si noti che GIL di Python è veramente solo un problema per CPython, l'implementazione di riferimento. Jython e IronPython non hanno un GIL. Come sviluppatore Python, non è in genere incontra la GIL a meno che non si sta scrivendo un estensione C. gli autori di estensioni C devono rilasciare il GIL quando le loro estensioni fare il blocco di I / O, in modo che gli altri thread nel processo Python hanno la possibilità di eseguire.

Altri suggerimenti

Si supponga di avere più thread che non fanno davvero toccare i dati di ciascuno. Quelli dovrebbero eseguire più indipendente possibile. Se si dispone di un "blocco globale", che è necessario acquisire al fine di (diciamo) chiamare una funzione, che può finire come un collo di bottiglia. Si può finire non ottenere molto beneficio da avere più thread in primo luogo.

Per dirla in un'analogia mondo reale: immaginate 100 sviluppatori che lavorano in una società con una sola tazza di caffè. La maggior parte degli sviluppatori sarebbe trascorrere il loro tempo in attesa di caffè invece di codifica.

Niente di tutto questo è Python-specifica - Non conosco i dettagli di ciò che aveva bisogno di un Python GIL per in primo luogo. Tuttavia, speriamo che ti ha dato una migliore idea del concetto generale.

si deve prima capire che cosa il pitone GIL prevede:

Ogni operazione / istruzione viene eseguita in l'interprete. GIL assicura che interprete è tenuto da un unico filo a un particolare istante di tempo . E il vostro programma di pitone con più thread lavora in un unico interprete. In un particolare istante di tempo, questo interprete è tenuto da un unico filo. Ciò significa che solo il filo che tiene l'interprete è eseguendo a ogni istante di tempo .

Ora, perché è un problema che:

La macchina potrebbe essere avere più core / processori. E più core consentono più thread di eseguire simultaneamente cioè più thread potrebbero eseguire in qualsiasi particolare istante di tempo. . Ma dal momento che l'interprete è detenuto da un unico filo, altri thread non stanno facendo nulla anche se hanno accesso a un nucleo. Quindi, non si ottiene alcun vantaggio offerto da più core perché in ogni istante solo un singolo nucleo, che è il nucleo essendo utilizzata dal thread attualmente tiene l'interprete, viene utilizzato. Quindi, il programma avrà tutto il tempo per l'esecuzione, come se si trattasse di un unico programma filettato.

Tuttavia, operazioni di lunga durata, quali I / O, elaborazione di immagini potenzialmente blocco o, e il numero NumPy macinare, accade fuori della GIL. Tratto da qui . Quindi, per tali operazioni, un'operazione multithread sarà ancora più veloce di una singola operazione filettato nonostante la presenza di GIL. Quindi, GIL non è sempre un collo di bottiglia.

Modifica: GIL è un dettaglio di implementazione di CPython. IronPython e Jython non hanno GIL, quindi un programma veramente multithread dovrebbe essere possibile in loro, il pensiero non ho mai usato PyPy e Jython e non sono sicuro di questo.

Python non permette il multi-threading nel vero senso della parola. Ha un pacchetto multi-threading, ma se si vuole multi-thread per accelerare il vostro codice, allora di solito non è una buona idea usarlo. Python ha un costrutto chiamato il Global Interpreter Lock (GIL).

https://www.youtube.com/watch?v=ph374fJqFPE

La GIL fa in modo che solo uno dei 'thread' in grado di eseguire in qualsiasi momento. Un filo acquisisce la GIL, fa un po 'di lavoro, quindi passa il GIL sulla filettatura successiva. Questo avviene molto rapidamente, in modo per l'occhio umano può sembrare le discussioni che sono in esecuzione in parallelo, ma sono in realtà solo a turno con lo stesso core della CPU. Tutto questo GIL passaggio overhead aggiunge esecuzione. Ciò significa che se si desidera rendere il vostro codice più veloce quindi utilizzando il pacchetto di threading, spesso non è una buona idea.

Non ci sono motivi per utilizzare il pacchetto di threading di Python. Se si desidera eseguire alcune cose contemporaneamente, e l'efficienza non è una preoccupazione, allora è totalmente soddisfacente e conveniente. Oppure, se si esegue il codice che deve aspettare qualcosa (come un IO), allora si potrebbe fare un sacco di senso. Ma la libreria di threading voleva far si utilizza CPU core aggiuntivi.

Multi-threading possono essere esternalizzate al sistema operativo (facendo multi-processing), qualche applicazione esterna che chiama il codice Python (ad esempio, una scintilla o Hadoop), o qualche codice che le chiamate di codice Python (per esempio: si potrebbe avere il vostro codice Python chiamata una funzione C che fa la roba multi-threaded costoso).

Ogni volta che due thread hanno accesso alla stessa variabile avete un problema. In C ++ per esempio, il modo per evitare il problema è quello di definire alcuni blocco mutex per evitare due thread, diciamo, immettere il setter di un oggetto allo stesso tempo.

Multithreading è possibile in pitone, ma due fili non può essere eseguita contemporaneamente ad una più fine granularità di un'istruzione di pitone. Il filo conduttore è sempre un blocco globale chiamato GIL.

Questo significa che se si inizia scrivere del codice multithreading al fine di sfruttare il processore multicore, le prestazioni non migliorerà. La soluzione al solito è costituito da andare multiprocesso.

Si noti che è possibile rilasciare il GIL se sei all'interno di un metodo che hai scritto in C, per esempio.

L'uso di un GIL non è inerente a Python, ma per alcuni dei suoi interpreti, tra cui la CPython più comune. (#Edited, vedi commento)

Il problema GIL è ancora valido in Python 3000.

Python 3.7 documentazione

Vorrei anche sottolineare la seguente citazione dal Python threading documentazione :

  

dettaglio implementativo CPython: In CPython, causa l'interprete di blocco globale, solo un thread può eseguire codice Python contemporaneamente (anche se alcune librerie di prestazioni orientati potrebbe superare questa limitazione). Se si desidera che l'applicazione per fare un uso migliore delle risorse di calcolo delle macchine multi-core, si consiglia di utilizzare multiprocessing o concurrent.futures.ProcessPoolExecutor. Tuttavia, threading è ancora un modello appropriato se si desidera eseguire più attività di I / O-bound contemporaneamente.

Questa fermate dei voce del glossario global interpreter lock che spiega che la GIL implica che il parallelismo filettato Python è adatto per CPU bound compiti :

  

Il meccanismo utilizzato dall'interprete CPython per assicurare che solo un thread esegue pitone bytecode alla volta. Ciò semplifica l'implementazione CPython rendendo il modello di oggetti (compresi i tipi critici incorporati come dict) implicitamente sicurezza contro l'accesso simultaneo. L'intero blocco interprete rende più facile per l'interprete di essere multi-thread, a scapito di gran parte del parallelismo offerto dalle macchine multiprocessore.

     

Tuttavia, alcuni moduli di estensione, sia standard o di terze parti, sono progettati in modo tale da liberare il GIL quando si eseguono compiti computazionalmente intensive quali la compressione o di hashing. Inoltre, la GIL viene sempre rilasciato quando si fa di I / O.

     

passato gli sforzi per creare un interprete di “libero-threaded” (un blocco dei dati condivisi in una granularità molto più fine) non hanno avuto successo perché le prestazioni sofferto nel caso comune a singolo processore. Si ritiene che per superare questo problema di prestazioni renderebbe l'applicazione molto più complicato e quindi più costoso da mantenere.

Questa citazione implica anche che dicts e quindi dell'assegnazione variabili sono thread-safe come un dettaglio attuazione CPython:

Successivamente, i documentazione per il pacchetto multiprocessing spiegare come supera il GIL dal processo riproduttivo esponendo un'interfaccia simile a quella di threading:

  

multiprocessore è un pacchetto che supporta i processi di deposizione utilizzando un'API simile al modulo filettatura. Il pacchetto multiprocessing offre sia concorrenza locale e remoto, efficace schivare l'interprete blocco globale utilizzando sottoprocessi invece di fili. A causa di questo, il modulo multiprocessing permette al programmatore di sfruttare più processori su una determinata macchina. Funziona su Unix e Windows.

e il documentazione per concurrent.futures.ProcessPoolExecutor spiegano che utilizza multiprocessing come back-end:

  

La classe ProcessPoolExecutor è una sottoclasse esecutore che utilizza un pool di processi per eseguire le chiamate in modo asincrono. ProcessPoolExecutor utilizza il modulo multiprocessing, che le consente di eludere il Global Interpreter Lock, ma significa anche che solo gli oggetti serializzabili possono essere eseguiti e restituiti.

che dovrebbe essere contrapposto all'altro classe ThreadPoolExecutor base che utilizza discussioni invece di processi

  

ThreadPoolExecutor è una sottoclasse esecutore che utilizza un pool di thread per eseguire chiamate in modo asincrono.

da cui possiamo concludere che ThreadPoolExecutor è adatto solo per I compiti / O bound, mentre ProcessPoolExecutor può anche gestire CPU compiti legati.

Il seguente domanda chiede perché la GIL esiste in primo luogo: Perché il Global Interpreter Lock?

Processo vs esperimenti filo

Multiprocessing vs Threading Python ho fatto un'analisi sperimentale di processo vs thread in Python.

anteprima rapida dei risultati:

entrare descrizione dell'immagine qui

Perché Python (CPython e altri) utilizza il GIL

http://wiki.python.org/moin/GlobalInterpreterLock

In CPython, il blocco interprete globale, o GIL, è un mutex che impedisce più thread nativi di eseguire bytecode Python contemporaneamente. Questo blocco è necessario soprattutto perché la gestione della memoria del CPython non è thread-safe.

Come rimuovere da Python?

Come Lua, forse Python potrebbe iniziare più VM, ma Python non farlo, credo che ci dovrebbero essere alcuni altri motivi.

In Numpy o qualche altra libreria python esteso, a volte, rilasciando il GIL ad altri thread potrebbe aumentare l'efficienza di tutto il programma.

Voglio condividere un esempio dal multithreading libro per gli effetti visivi. Così qui è una situazione dead lock classica

static void MyCallback(const Context &context){
Auto<Lock> lock(GetMyMutexFromContext(context));
...
EvalMyPythonString(str); //A function that takes the GIL
...    
}

Ora consideriamo gli eventi nella sequenza risultante di un morto-lock.

╔═══╦════════════════════════════════════════╦══════════════════════════════════════╗
║   ║ Main Thread                            ║ Other Thread                         ║
╠═══╬════════════════════════════════════════╬══════════════════════════════════════╣
║ 1 ║ Python Command acquires GIL            ║ Work started                         ║
║ 2 ║ Computation requested                  ║ MyCallback runs and acquires MyMutex ║
║ 3 ║                                        ║ MyCallback now waits for GIL         ║
║ 4 ║ MyCallback runs and waits for MyMutex  ║ waiting for GIL                      ║
╚═══╩════════════════════════════════════════╩══════════════════════════════════════╝
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top