Pourquoi Python a-t-il été écrit avec le GIL?

112

Le verrou d'interprète global (GIL) semble être souvent cité comme l'une des principales raisons pour lesquelles enfiler des objets, entre autres, est un problème délicat en Python - ce qui soulève la question "Pourquoi cela a-t-il été fait?"

Étant pas un programmeur, je ne sais pas pourquoi cela pourrait être - quelle était la logique derrière la mise en place du GIL?

Fomite
la source
10
L' article de Wikipedia stipule que "le GIL peut constituer un obstacle important au parallélisme - un prix payé pour le dynamisme de la langue" , et poursuit: "Les raisons pour utiliser un tel verrou sont notamment les suivantes: vitesse accrue des programmes à thread unique (pas besoin d'acquérir ou de libérer des verrous sur toutes les structures de données séparément), et une intégration facile des bibliothèques C qui ne sont généralement pas thread-safe. "
Robert Harvey
3
@RobertHarvey, le dynamisme n'a rien à voir avec cela. Le problème est la mutation.
dan_waterworth
stackoverflow.com/questions/265687/…
Ciro Santilli a annoncé le 10/10
1
Je ne peux pas m'empêcher de penser que, contrairement au manque de chiffres non signés de Java, l'objectif était d'empêcher les personnes qui ne savent pas ce qu'elles font de se tirer une balle dans le pied. Malheureusement, tous ceux qui ne savent ce qu'ils font obtient une langue déficiente, ce qui est vraiment dommage car les roches Python dans tant d'autres moyens
base
1
@Basic, il doit exister un moyen standard de traiter les tableaux d'octets en Java (je ne l'ai pas utilisé depuis longtemps) pour pouvoir effectuer des calculs cryptographiques. Python (par exemple) n'a pas de nombres signés, mais je n'essayerais même pas de faire des opérations au niveau des bits avec elle car il existe de meilleures façons.
Nick T

Réponses:

105

Il existe plusieurs implémentations de Python, par exemple, CPython, IronPython, RPython, etc.

Certains d'entre eux ont un GIL, d'autres non. Par exemple, CPython a le GIL:

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

Les applications écrites dans des langages de programmation avec GIL peuvent être conçues pour utiliser des processus distincts pour obtenir un parallélisme complet, chaque processus ayant son propre interpréteur et à son tour son propre GIL.

Avantages du GIL

  • Augmentation de la vitesse des programmes mono-thread.
  • Intégration facile de bibliothèques C qui ne sont généralement pas thread-safe.

Pourquoi Python (CPython et autres) utilise le GIL

Dans CPython, le verrou d'interpréteur global, ou GIL, est un mutex qui empêche plusieurs threads natifs d'exécuter en même temps des bytecodes Python. Ce verrou est nécessaire principalement parce que la gestion de la mémoire de CPython n'est pas thread-safe.

La GIL est controversée car elle empêche les programmes CPython multithreads de tirer pleinement parti des systèmes multiprocesseurs dans certaines situations. Notez que les opérations potentiellement bloquantes ou de longue durée, telles que les E / S, le traitement des images et la réduction du nombre de numéros NumPy, se produisent en dehors de la liste GIL. Par conséquent, ce n'est que dans les programmes multithreads qui passent beaucoup de temps dans GIL, interprétant le bytecode CPython, que GIL devient un goulot d'étranglement.

Python a un code GIL par opposition à un verrouillage à grain fin pour plusieurs raisons:

  • C'est plus rapide dans le cas des threads simples.

  • C'est plus rapide dans le cas multithread pour les programmes liés d'E / S.

  • Il est plus rapide dans le cas multithread des programmes liés à l'unité centrale qui effectuent leur travail de calcul intensif dans les bibliothèques C.

  • Cela facilite l’écriture des extensions C: il n’y aura pas de changement de threads Python, sauf si vous le permettez (c’est-à-dire entre les macros Py_BEGIN_ALLOW_THREADS et Py_END_ALLOW_THREADS).

  • Cela facilite l’emballage des bibliothèques C. Vous n'avez pas à vous soucier de la sécurité du fil. Si la bibliothèque n’est pas thread-safe, vous devez simplement verrouiller la GIL pendant que vous l’appelez.

Le GIL peut être publié par les extensions C. La bibliothèque standard de Python libère le GIL autour de chaque appel d'E / S bloquant. Ainsi, le GIL n'a aucune conséquence sur les performances des serveurs liés d'E / S. Vous pouvez ainsi créer des serveurs de réseau en Python à l'aide de processus (fork), de threads ou d'entrées / sorties asynchrones. GIL ne vous gênera pas.

Les bibliothèques numériques en C ou en Fortran peuvent également être appelées avec la version GIL publiée. Pendant que votre extension C attend la fin d'une FFT, l'interpréteur exécutera d'autres threads Python. Un GIL est donc plus facile et plus rapide qu'un verrouillage fin dans ce cas également. Ceci constitue l'essentiel du travail numérique. L'extension NumPy libère le GIL chaque fois que possible.

Les threads sont généralement un mauvais moyen d'écrire la plupart des programmes serveur. Si la charge est faible, le forgeage est plus facile. Si la charge est élevée, la programmation asynchrone des entrées / sorties et des événements (par exemple, à l'aide de la structure Twisted de Python) est préférable. La seule excuse pour utiliser des threads est le manque de os.fork sous Windows.

La GIL pose un problème si, et seulement si, vous effectuez un travail gourmand en ressources processeur en Python pur. Ici, vous pouvez obtenir une conception plus propre en utilisant des processus et la transmission de messages (par exemple, mpi4py). Il existe également un module de «traitement» dans la fromagerie Python, qui donne aux processus la même interface que les threads (c'est-à-dire, remplacez threading.Thread par processing.Process).

Les threads peuvent être utilisés pour maintenir la réactivité d'une interface graphique indépendamment de la GIL. Si le GIL altère votre performance (voir la discussion ci-dessus), vous pouvez laisser votre thread générer un processus et attendre qu'il se termine.

Md Mahbubur Rahman
la source
52
Cela ressemble à du raisin aigre pour moi. Python ne peut pas faire les threads correctement, vous devez donc expliquer pourquoi les threads sont inutiles, voire mauvais. "Si la charge est faible, le forgeage est plus facile", sérieusement? Et le GIL est "plus rapide" pour tous ces cas uniquement si vous insistez pour utiliser le calculateur de référence par comptage.
Michael Borgwardt
9
s/RPython/PyPy/g. @MichaelBorgwardt Donner des raisons pour GIL est un peu le but de la question, n'est-ce pas? Bien que je sois d’accord, une partie du contenu de cette réponse (à savoir une discussion sur les alternatives) n’est pas pertinente. Et pour le meilleur ou pour le pire, il est maintenant presque impossible de se débarrasser du recomptage - il est profondément enraciné dans l'ensemble de l'API et de la base de code; il est presque impossible de s'en débarrasser sans réécrire la moitié du code et casser tout le code externe.
10
N'oubliez pas la multiprocessingbibliothèque - standard depuis 2.6. Ses pools de travailleurs sont une abstraction ultra-lisse pour certains types simples de parallélisme.
Sean McSomething
8
@alcalde Seulement si vous ne savez pas ce que vous faites et / ou si vous ne voulez pas que vos threads puissent travailler en coopération / communiquer. Autrement, c’est une douleur royale à l’arrière, surtout si l’on prend en compte le temps de lancement d’un nouveau processus sur certains systèmes d’exploitation. Nous avons des serveurs avec 32 cœurs, alors pour les utiliser pleinement dans CPython, il me faudrait 32 processus. Ce n'est pas une "bonne solution", c'est un bidouillage pour pallier les insuffisances de CPython.
Base
8
Le fait que des threads existent sur des plates-formes autres que Windows devrait être une preuve suffisante que la forking ne convient pas dans toutes les situations.
zneak
42

Tout d'abord: Python n'a pas de GIL. Python est un langage de programmation. Un langage de programmation est un ensemble de règles et de restrictions mathématiques abstraites. Il n'y a rien dans la spécification du langage Python qui indique qu'il doit y avoir un GIL.

Il existe de nombreuses implémentations différentes de Python. Certains ont un GIL, d'autres non.

Une explication simple pour avoir un GIL est qu'il est difficile d'écrire du code simultané. En plaçant un verrou géant autour de votre code, vous le forcez à toujours fonctionner en série. Problème résolu!

Dans CPython, en particulier, un objectif important est de faciliter l’extension de l’interprète avec des plugins écrits en C. Une fois encore, l’écriture de code simultané est difficile; ainsi, en garantissant qu’il n’y aura pas d’accès simultané, il sera plus facile l'interprète. De plus, beaucoup de ces extensions ne sont que des maigres enveloppants autour de bibliothèques existantes qui n’ont peut-être pas été écrites avec l’accès simultané à l’esprit.

Jörg W Mittag
la source
6
C'est le même argument que l'absence de types numériques non signés en Java - les développeurs pensent que tous les autres sont plus stupides qu'ils ne le sont ...
Basic
1
@Basic - croyez-le ou non, même lorsque vous n'êtes pas vraiment, vraiment idiot, il est utile de disposer d'un langage qui simplifie les hypothèses qui vous empêchent de penser à certaines choses pour les faire fonctionner. chose. CPython est idéal pour certaines choses, y compris les applications multithread simples (où le programme est lié à l'IO, ce qui est le cas de beaucoup, et par conséquent, le GIL importe peu), car les décisions de conception qui ont fait de GIL la meilleure solution facilitent également la programmation de ces applications. , en particulier le fait qu’il supporte les opérations atomiques sur les collections .
Jules
@ Jules Oui, c'est très pratique jusqu'à ce que vous ayez besoin de ces fonctionnalités. La solution "préférée" de cpython consistant à "juste l'écrire dans un autre langage comme c ++" signifie alors que vous perdez tous les avantages d'un python isolé. Si vous écrivez la moitié de votre code en c ++, pourquoi partir de Python? Bien sûr, pour les petits projets API / colle, c'est rapide et facile, et pour ETL, il est incomparable, mais il ne convient pas à tout ce qui nécessite de lourdes charges. Même chose que d’utiliser Java pour parler au matériel… C’est presque comique que les cerceaux que vous devez franchir.
base
16

Quel est le but d'un GIL?

La documentation CAPI dit ceci à ce sujet:

L'interpréteur Python n'est pas totalement thread-safe. Afin de prendre en charge les programmes Python multithreads, il existe un verrou global, appelé verrou d'interpréteur global ou GIL, qui doit être maintenu par le thread actuel avant qu'il puisse accéder en toute sécurité aux objets Python. Sans le verrou, même les opérations les plus simples pourraient poser des problèmes dans un programme multithread: par exemple, lorsque deux threads incrémentent simultanément le compte de références du même objet, le compte de références peut finir par être incrémenté une fois au lieu de deux.

En d'autres termes, le GIL prévient la corruption d'État. Les programmes Python ne doivent jamais générer d'erreur de segmentation, car seules les opérations sécurisées pour la mémoire sont autorisées. Le GIL étend cette assurance aux programmes multithreads.

Quelles sont les alternatives?

Si le but du GIL est de protéger l’État de la corruption, une alternative évidente consiste à verrouiller le grain beaucoup plus fin; peut-être au niveau de chaque objet. Le problème, c’est que, même s’il a été prouvé que les programmes multithreads permettaient d’accroître les performances, il en résultait plus de frais généraux et que les programmes mono-thread en souffraient.

dan_waterworth
la source
2
Il serait bon de laisser un utilisateur exécuter un programme avec une option interpréteur remplaçant le gil pour un verrou à grain fin, et savoir en quelque sorte en lecture seule si le processus actuel a été déclenché avec ou sans gil.
Luis Masuelli
Malgré GIL, j’ai réussi à produire une erreur de segmentation dans un programme multithread en raison d’une utilisation négligente du module pyodbc. Ainsi, "ne devrait jamais produire une faute de segmentation" est une erreur.
Muposat