Maison > développement back-end > Tutoriel Python > Python, la passerelle vers le multithreading hautes performances sans GIL

Python, la passerelle vers le multithreading hautes performances sans GIL

Mary-Kate Olsen
Libérer: 2025-01-06 16:58:41
original
706 Les gens l'ont consulté

Introduction

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.

Qu'est-ce que le verrouillage global de l'interprète (GIL)

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.

Pourquoi le GIL est un goulot d'étranglement pour le multithreading

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.

Python 3.13 : débloquer le multithreading avec GIL désactivé

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.

Conditions préalables à l'utilisation de Python 3.13 sans GIL

  • Code source Python 3.13 : La désactivation du GIL n'est pas disponible dans les binaires standard prédéfinis. Vous devez construire Python 3.13 à partir de la source avec l'indicateur --disable-gil.
  • CPU multicœur : Vous avez besoin d'un processeur multicœur pour bénéficier d'un véritable multithreading, car les threads s'exécuteront désormais en parallèle sur plusieurs cœurs.

Compilation de Python 3.13 avec GIL désactivé

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

Étape par étape

  • Télécharger le code source de Python 3.13 Vous devez d’abord télécharger l’archive tar du code source Python 3.13 depuis le site officiel de Python. En effet, les binaires prédéfinis (comme ceux que vous téléchargez directement depuis python.org) ne sont pas compilés avec la prise en charge de la désactivation du GIL. Vous pouvez le télécharger en utilisant le navigateur Web ou en utilisant wget ou même en utilisant curl dans votre terminal
wget https://www.python.org/ftp/python/3.13.0/Python-3.13.0.tgz
Copier après la connexion
  • Extraire la source :
tar -xf Python-3.13.0.tgz
cd Python-3.13.0
Copier après la connexion
  • Configurez le build avec --disable-gil Vous devez configurer Python avec --disable-gil pour prendre en charge l'option de désactivation du GIL.
./configure --disable-gil
Copier après la connexion
  • Compilez et installez Python :
make
sudo make altinstall 
Copier après la connexion
  • Si l'étape altinstall échoue, réexécutez la commande configure avec --prefix
./configure --disable-gil --prefix=$HOME/python3.13
Copier après la connexion
  • Exécutez make altinstall dans le répertoire spécifié Ensuite, exécutez la commande make altinstall
make altinstall
Copier après la connexion

Comment détecter GIL dans Python 3.13

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()
Copier après la connexion

Pratique : Python multithreading avec GIL vs GIL-Free

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()

Copier après la connexion

Analyse:

## 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.
Copier après la connexion

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.

Python The Gateway to High-Performance Multithreading Without GIL

Python The Gateway to High-Performance Multithreading Without GIL

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.

Considérations clés avant de désactiver le GIL

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.

Dépôt GitHub

Vous pouvez trouver le code source complet des exemples dans ce blog sur mon GitHub :

Analyse des performances Python GIL

Clause de non-responsabilité:

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!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal