12 pontos por darjeeling 2026-01-31 | Ainda não há comentários. | Compartilhar no WhatsApp

Resumo:

  • O módulo subprocess do Python e a biblioteca psutil usaram, nos últimos 15 anos, uma abordagem ineficiente de “busy-loop polling” ao esperar o encerramento de processos (wait()), repetindo sleep e waitpid.
  • Esse método causa wake-ups desnecessários da CPU, consumo de bateria, latência na detecção do término de processos e tem baixa escalabilidade ao monitorar muitos processos.
  • Com atualizações recentes, no Linux passou a ser implementada uma espera realmente orientada a eventos usando pidfd_open() e poll(), e em BSD/macOS usando kqueue().
  • O Windows já usava WaitForSingleObject, então não houve mudanças, mas em sistemas POSIX os context switches desnecessários foram eliminados e o uso de CPU tende a “0”.

Resumo detalhado:
1. O problema que durou 15 anos: busy-loop polling
Desde que o parâmetro timeout foi adicionado ao subprocess.Popen.wait() no Python 3.3, a biblioteca padrão do Python e a amplamente usada biblioteca psutil vinham usando uma abordagem ineficiente para esperar o término de processos.

A lógica anterior era simples, mas ineficiente:

  1. Verificar o estado do processo com waitpid(WNOHANG) (não bloqueante)
  2. Se ainda não tiver terminado, fazer um sleep() curto (com exponential backoff)
  3. Voltar ao passo 1 e repetir
# método anterior (código conceitual)  
import time, os  
  
def wait_busy(pid, timeout):  
    delay = 0.0001  
    while True:  
        # verifica se o processo terminou (polling)  
        if os.waitpid(pid, os.WNOHANG) == (pid, status):  
            return status  
        time.sleep(delay)  
        delay = min(delay * 2, 0.040) # aumenta o tempo de espera até 40 ms no máximo  
  

Esse método tem três desvantagens críticas.

  • Wake-ups da CPU: por maior que seja o tempo de espera, o sistema ainda precisa acordar periodicamente para verificar o estado, desperdiçando ciclos de CPU e consumindo energia.
  • Latência: há um intervalo inevitável entre o momento em que o processo realmente termina e o momento em que isso é detectado após sair do sleep.
  • Escalabilidade: em ambientes de servidor que precisam monitorar centenas ou milhares de processos ao mesmo tempo, esse overhead cresce rapidamente.

2. A solução: espera orientada a eventos para sistemas POSIX
Todos os sistemas POSIX oferecem mecanismos (select, poll, epoll, kqueue) para detectar mudanças de estado em descritores de arquivo. Recentemente, Python e psutil foram aprimorados para usar isso também na detecção de PIDs de processos.

  • Linux: usa a system call pidfd_open(), introduzida no kernel Linux 5.3 em 2019. Ela retorna um descritor de arquivo que aponta para o PID do processo, permitindo registrar esse descritor em poll() ou epoll() para monitorar o evento de término do processo. (Adicionada ao módulo os a partir do Python 3.9)
  • BSD / macOS: usa o filtro EVFILT_PROC da system call kqueue() para monitorar eventos de processo com eficiência.
  • Windows: já suportava espera orientada a eventos por meio da API WaitForSingleObject, então não houve mudanças.

3. Melhorias de desempenho e resultados
Com essa mudança, durante a chamada de wait() o processo fica, do ponto de vista do kernel, em estado de “interruptible sleep”. Ou seja, sem consumir CPU alguma, ele aguarda silenciosamente no espaço do kernel e desperta imediatamente quando o sinal de término do processo ocorre.

Segundo benchmarks feitos com /usr/bin/time -v e ferramentas semelhantes, em comparação com o método anterior houve uma redução drástica nos context switches desnecessários, e a velocidade de detecção do término do processo também melhorou de forma imediata. Essa atualização foi incorporada à biblioteca psutil e ao núcleo do CPython, de modo que, daqui para frente, desenvolvedores Python poderão aproveitar o ganho de desempenho sem precisar alterar seus próprios códigos.

Ainda não há comentários.

Ainda não há comentários.