12 points par darjeeling 2026-01-31 | Aucun commentaire pour le moment. | Partager sur WhatsApp

Résumé :

  • Le module subprocess de Python et la bibliothèque psutil utilisent depuis 15 ans une méthode inefficace de « polling en busy loop » lors de l’attente de la fin d’un processus (wait()), en répétant sleep et waitpid.
  • Cette approche entraîne des réveils CPU inutiles, une consommation de batterie accrue, une latence dans la détection de la fin des processus, et passe mal à l’échelle lorsqu’il faut surveiller de nombreux processus.
  • Une mise à jour récente introduit enfin une vraie attente événementielle sur Linux via pidfd_open() et poll(), et sur BSD/macOS via kqueue().
  • Windows utilisait déjà WaitForSingleObject, donc rien ne change de ce côté-là, mais sur les systèmes POSIX, les changements de contexte inutiles disparaissent et l’usage CPU en attente converge vers 0.

Résumé détaillé :
1. Un problème qui a duré 15 ans : le polling en busy loop
Depuis l’ajout du paramètre timeout à subprocess.Popen.wait() dans Python 3.3, la bibliothèque standard de Python ainsi que la très utilisée bibliothèque psutil reposent sur une méthode inefficace pour attendre la fin d’un processus.

L’ancienne logique était simple, mais peu efficace :

  1. Vérifier l’état du processus avec waitpid(WNOHANG) (non bloquant)
  2. S’il n’est pas terminé, faire un court sleep() (avec exponential backoff)
  3. Revenir à l’étape 1 et répéter
# Ancienne approche (code conceptuel)  
import time, os  
  
def wait_busy(pid, timeout):  
    delay = 0.0001  
    while True:  
        # Vérifie si le processus est terminé (polling)  
        if os.waitpid(pid, os.WNOHANG) == (pid, status):  
            return status  
        time.sleep(delay)  
        delay = min(delay * 2, 0.040) # Augmente l’attente jusqu’à 40 ms max  
  

Cette méthode présente trois défauts majeurs.

  • Réveils CPU : même en augmentant le délai d’attente, le système doit se réveiller périodiquement pour vérifier l’état, ce qui gaspille des cycles CPU et de l’énergie.
  • Latence : il existe inévitablement un décalage entre le moment réel où le processus se termine et celui où cette fin est détectée après le réveil du sleep.
  • Passage à l’échelle : dans les environnements serveur où il faut surveiller simultanément des centaines ou des milliers de processus, ce surcoût augmente rapidement.

2. La solution : une attente événementielle pour les systèmes POSIX
Tous les systèmes POSIX fournissent des mécanismes de détection des changements d’état des descripteurs de fichiers (select, poll, epoll, kqueue). Python et psutil ont récemment été améliorés pour réutiliser ces mécanismes afin de détecter l’état des PID.

  • Linux : utilisation de l’appel système pidfd_open(), introduit dans le noyau Linux 5.3 en 2019. Il renvoie un descripteur de fichier pointant vers un PID, qui peut ensuite être enregistré dans poll() ou epoll() pour surveiller l’événement de fin du processus. (Ajouté au module os à partir de Python 3.9)
  • BSD / macOS : utilisation du filtre EVFILT_PROC de l’appel système kqueue() pour surveiller efficacement les événements liés aux processus.
  • Windows : l’attente événementielle était déjà prise en charge via l’API WaitForSingleObject, donc aucun changement ici.

3. Gains de performance et résultats
Avec ce changement, lors d’un appel à wait(), le processus passe du point de vue du noyau dans un état d’« interruptible sleep ». Autrement dit, il attend silencieusement dans l’espace noyau sans consommer du tout de CPU, puis se réveille immédiatement quand le signal de fin du processus survient.

D’après les benchmarks réalisés notamment avec /usr/bin/time -v, les changements de contexte inutiles ont fortement diminué par rapport à l’ancienne méthode, et la vitesse de détection de fin de processus s’est elle aussi améliorée de façon immédiate. Cette mise à jour a été intégrée à la bibliothèque psutil et au cœur de CPython, ce qui permettra aux développeurs Python d’en profiter sans modification particulière de leur code.

Aucun commentaire pour le moment.

Aucun commentaire pour le moment.