Python est connu depuis longtemps pour sa facilité d'utilisation et sa polyvalence, mais un sujet qui a suscité de nombreuses discussions au sein de la communauté Python est le Global Interpreter Lock (GIL). Le GIL a été à la fois une protection et un goulot d'étranglement pour le modèle de concurrence de Python, en particulier pour les tâches liées au processeur qui pourraient autrement tirer parti de plusieurs cœurs de processeur. Cependant, avec la sortie de Python 3.13, les développeurs Python disposent d'une nouvelle option révolutionnaire : la possibilité de désactiver le GIL. Ce blog explorera ce qu'est le GIL, pourquoi il a constitué un obstacle aux performances en multithreading et comment détecter et désactiver le GIL dans Python 3.13 pour débloquer de véritables performances multithreading.
Le Global Interpreter Lock (GIL) est un mutex qui protège l'accès aux objets Python, empêchant plusieurs threads natifs d'exécuter le bytecode Python à la fois. Cela garantit la sécurité des threads pour les programmes Python, mais au prix d'une exécution simultanée. Le GIL rend les threads Python plus efficaces pour les tâches liées aux E/S mais limite leurs performances pour les tâches liées au CPU.
Le GIL de Python permet à un seul thread de s'exécuter simultanément, même dans les programmes multithread. Bien que cela convienne pour les tâches liées aux E/S où le programme attend des opérations d'entrée/sortie, cela limite considérablement les performances des tâches liées au processeur telles que le calcul des nombres, l'analyse des données ou le traitement d'images.
Avec Python 3.13, les développeurs ont la possibilité de désactiver le GIL pendant le processus de construction de Python. Cependant, la désactivation du GIL n'est pas disponible dans les distributions Python prédéfinies. Au lieu de cela, vous devez compiler Python 3.13 à partir des sources avec l'option --disable-gil.
Cette nouvelle option ouvre la porte à un véritable parallélisme dans les tâches multithread liées au processeur, permettant aux threads de s'exécuter en parallèle sur plusieurs cœurs.
Pour désactiver le GIL à l'aide de l'indicateur -X gil=0, vous devez compiler Python à partir des sources avec l'indicateur --disable-gil activé. Voici comment procéder
wget https://www.python.org/ftp/python/3.13.0/Python-3.13.0.tgz
tar -xf Python-3.13.0.tgz cd Python-3.13.0
./configure --disable-gil
make sudo make altinstall
./configure --disable-gil --prefix=$HOME/python3.13
make altinstall
Dans Python 3.13, vous pouvez vérifier si le GIL est activé ou désactivé à l'aide de la fonction sys._is_gil_enabled().
import sys def check_gil_status(): if sys.version_info >= (3, 13): status = sys._is_gil_enabled() if status: print("GIL is currently enabled.") else: print("GIL is currently disabled.") else: print("Python version does not support GIL status detection.") check_gil_status()
Le code Python suivant a été développé pour évaluer les gains de performances lors de la désactivation du GIL dans Python 3.13. Le script exécute huit threads simultanément, chacun étant chargé de calculer les facteurs premiers de grands nombres. En tirant parti du véritable parallélisme, le code met en évidence les performances améliorées obtenues sans le GIL.
#!/usr/bin/env python3 import sys import sysconfig import time from threading import Thread from multiprocessing import Process # Decorator to measure execution time of functions def calculate_execution_time(func): def wrapper(*args, **kwargs): start_time = time.perf_counter() result = func(*args, **kwargs) end_time = time.perf_counter() execution_time = end_time - start_time print(f"{func.__name__} took {execution_time:.4f} seconds.") return result return wrapper # Compute-intensive task: Iterative Fibonacci calculation def compute_fibonacci(n): """Compute Fibonacci number for a given n iteratively.""" a, b = 0, 1 for _ in range(n): a, b = b, a + b return a # Single-threaded task execution @calculate_execution_time def run_single_threaded(nums): for num in nums: compute_fibonacci(num) # Multi-threaded task execution @calculate_execution_time def run_multi_threaded(nums): threads = [Thread(target=compute_fibonacci, args=(num,)) for num in nums] for thread in threads: thread.start() for thread in threads: thread.join() # Multi-processing task execution @calculate_execution_time def run_multi_processing(nums): processes = [Process(target=compute_fibonacci, args=(num,)) for num in nums] for process in processes: process.start() for process in processes: process.join() # Main execution function def main(): # Check Python version and GIL status for Python 3.13+ print(f"Python Version: {sys.version}") py_version = float(".".join(sys.version.split()[0].split(".")[:2])) status = sysconfig.get_config_var("Py_GIL_DISABLED") if py_version >= 3.13: status = sys._is_gil_enabled() if status is None: print("GIL cannot be disabled for Python <= 3.12") elif status == 0: print("GIL is currently disabled") elif status == 1: print("GIL is currently active") # Run tasks on the same input size for comparison nums = [300000] * 8 print("\nRunning Single-Threaded Task:") run_single_threaded(nums) print("\nRunning Multi-Threaded Task:") run_multi_threaded(nums) print("\nRunning Multi-Processing Task:") run_multi_processing(nums) if __name__ == "__main__": main()
## Python 3.13 with GIL Disabled Python Version: 3.13.0 experimental free-threading build (main, Oct 14 2024, 17:09:28) [Clang 14.0.0 (clang-1400.0.29.202)] GIL is currently disabled Running Single-Threaded Task: run_single_threaded took 8.6587 seconds. Running Multi-Threaded Task: run_multi_threaded took 1.3885 seconds. Running Multi-Processing Task: run_multi_processing took 1.5953 seconds. ## Python 3.13 with GIL Enabled Python Version: 3.13.0 experimental free-threading build (main, Oct 14 2024, 17:09:28) [Clang 14.0.0 (clang-1400.0.29.202)] GIL is currently active Running Single-Threaded Task: run_single_threaded took 8.7108 seconds. Running Multi-Threaded Task: run_multi_threaded took 8.6645 seconds. Running Multi-Processing Task: run_multi_processing took 1.4530 seconds. ## Python 3.12 Python Version: 3.12.6 (main, Sep 7 2024, 19:30:10) [Clang 14.0.0 (clang-1400.0.29.202)] GIL cannot be disabled for Python <= 3.12 Running Single-Threaded Task: run_single_threaded took 8.7004 seconds. Running Multi-Threaded Task: run_multi_threaded took 8.6297 seconds. Running Multi-Processing Task: run_multi_processing took 1.4876 seconds.
Performances multithread : Le véritable avantage de la désactivation du GIL est évident dans le scénario multithread :
Avec GIL désactivé (3.13), le temps d'exécution est de 1,5703 secondes.
Avec GIL activé (3.13), le temps d'exécution est de 8,5901 secondes.
Résultat : la désactivation du GIL a entraîné une amélioration des performances d'environ 81,7 % pour les tâches multithread.
Le graphique démontre clairement que la désactivation du GIL dans Python 3.13 entraîne une amélioration substantielle des performances pour les tâches multithread liées au processeur, permettant à Python d'utiliser efficacement plusieurs cœurs de processeur en parallèle. Alors que les performances monothread et multi-traitement restent largement inchangées, les performances multithread affichent une amélioration significative, faisant de Python 3.13 un tournant pour les applications gourmandes en CPU qui reposent sur le multi-threading.
Cependant, les versions Python antérieures à 3.13 ne supportent pas la désactivation du GIL, ce qui explique pourquoi leurs performances multithread restent similaires à celles de Python 3.13 avec le GIL activé. Cette limitation dans les versions antérieures continue de restreindre la capacité de Python à exploiter pleinement le multithreading pour les tâches liées au processeur.
La désactivation du Global Interpreter Lock (GIL) dans Python 3.13 peut débloquer des améliorations significatives des performances dans les tâches multithread liées au processeur. Cependant, il y a plusieurs facteurs importants à considérer avant de le faire :
Sécurité des threads : Sans le GIL, vous devez gérer manuellement la sécurité des threads à l'aide de verrous ou d'autres mécanismes de synchronisation pour éviter les conditions de concurrence dans votre code.
Dégradation potentielle des performances : Le verrouillage à granularité fine peut introduire des conflits, ce qui peut dégrader les performances des tâches monothread ou liées aux E/S qui bénéficiaient auparavant du GIL.
Compatibilité avec les bibliothèques tierces : De nombreuses extensions et bibliothèques C supposent la présence du GIL pour la sécurité des threads. La désactivation du GIL peut nécessiter des mises à jour de ces bibliothèques pour garantir qu'elles fonctionnent correctement dans un environnement multithread.
Gestion complexe de la mémoire : La désactivation du GIL introduit plus de complexité dans la gestion de la mémoire, nécessitant une gestion de la mémoire thread-safe, ce qui peut augmenter le risque de bugs et d'erreurs.
Tâches liées aux E/S : La désactivation du GIL offre des avantages limités pour les tâches liées aux E/S, où les mécanismes d'E/S non bloquants comme asyncio pourraient être plus efficaces.
Difficulté de débogage : Sans le GIL, le débogage des applications multithread peut devenir plus difficile en raison de la probabilité accrue de conditions de concurrence critique et de blocages.
Utilisation plus élevée de la mémoire : L'utilisation de verrous et la gestion des états des threads sans le GIL peuvent augmenter la consommation de mémoire, en particulier dans les applications multithread.
Systèmes embarqués : La désactivation du GIL pourrait compliquer l'intégration de Python avec les environnements multithread dans les systèmes embarqués, nécessitant plus d'efforts pour une intégration efficace.
Conflit de verrouillage : Dans certains cas, la désactivation du GIL peut entraîner un conflit de verrouillage entre les threads, ce qui peut réduire les améliorations de performances attendues.
Vous pouvez trouver le code source complet des exemples dans ce blog sur mon GitHub :
Analyse des performances Python GIL
Ceci est un blog personnel. Les points de vue et opinions exprimés ici sont uniquement ceux de l’auteur et ne représentent ceux d’aucune organisation ou individu avec lequel l’auteur pourrait être associé, professionnellement ou personnellement.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!