Ontem saiu o Python 3.13, uma das versões mais aguardadas do interpretador. A principal atração para mim sempre foi a possibilidade de rodar código em Python sem o GIL. O GIL, ou Global Interpreter Lock, assombrou o Python por muito tempo e sempre foi motivo de muitas discussões.

Há muito tempo vem se tentando remover o GIL do interpretador. Mas o que ele atrapalha? Quando rodamos um código com múltiplos threads em Python, o GIL garante que as estruturas comuns do interpretador estavam protegidas entre estes threads, ou seja, contra problemas de concorrência. Várias tentativas foram feitas, mas todas deixavam o Python mais lento ou só funcionavam em casos específicos. Um rant do Guido - It isn’t Easy to Remove GIL em resposta a este post Data Services and Integration, An open letter to Guido van Rossum: Mr Rossum, tear down that GIL!. Nada melhor que a PEP-703 para entender como estas conversas acabaram. Na realidade, não acabaram, o GIL pode ser ativado ou desativado e cada utilizador pode escolher qual versão do interpretador quer usar. Nem tudo são flores, pois várias bibliotecas ainda precisam de mais tempo para suportar esta opção e mesmo para estabilizarem seus códigos.

Você pode instalar o Python 3.13 e rodar o código abaixo para ver como ele funciona. Se sua distribuição Linux ainda não disponibilizar a versão 3.13, você pode experimentar usando Docker, compilar com pyenv e dentro de alguns dias usando o uv. Para Mac e Windows, basta baixar os instaladores no site oficial python.org.

Como saber em qual interpretador você está?#

Primeiro, baixe o Python 3.13 e rode o instalador. No caso do Windows, escolha customizar a instalação e a opção que instala o Python3.13t.

Após instalar o Python 3.13, você pode verificar qual versão está usando, chamando o interpretador na linha de comandos.

O Python 3.13 normal não exibe a palavra experimental após a versão.

> python
Python 3.13.0 (tags/v3.13.0:60403a5, Oct  7 2024, 09:38:07) [MSC v.1941 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

No Windows e no Mac, os executáveis da versão sem GIL são nomeados diferentemente. No Windows, você usa python3.13t para chamar a versão sem GIL.

> python3.13t
Python 3.13.0 experimental free-threading build (tags/v3.13.0:60403a5, Oct  7 2024, 09:53:29) [MSC v.1941 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

Veja que as palavras experimental free-threading aparecem após o número da versão.

No Linux, faça:

PYTHON_GIL=1 python

ou

python -X gil=0

Claro, após instalar o Python 3.13 e adaptar o comando para como você chama o interpretador. Provavelmente algo com python3.13.

Um pequeno teste#

Vamos rodar este pequeno programa de teste para verificar as diferenças entre as versões 3.13 com e sem GIL. Ele gera uma pequena carga de processamento e a executa com e sem threads. O tempo de cada execução é exibido no final de cada execução. O programa detecta quantos cores você tem no sistema. Você pode aumentar o valor de CARGA se rodar muito rápido no seu computador.

import time
import threading
import multiprocessing
import contextlib
from rich import print

CARGA = 10_000_000


def trabalho(name):
    print(f" + Trabalho {name} iniciando")
    z = 0
    for i in range(CARGA):
        z += i
    print(f" - Trabalho {name} acabando")


@contextlib.contextmanager
def cronometro(mensagem):
    print(mensagem)
    start_time = time.time()
    yield
    end_time = time.time()
    print(f"Tempo: {end_time - start_time}")


def com_threads():
    threads = []
    for i in range(multiprocessing.cpu_count()):
        threads.append(threading.Thread(target=trabalho, args=(i,)))
    for thread in threads:
        thread.start()
    for thread in threads:
        thread.join()


def sem_threads():
    for z in range(multiprocessing.cpu_count()):
        trabalho(z)


if __name__ == "__main__":
    with cronometro("Com threads"):
        com_threads()
    with cronometro("Sem threads"):
        sem_threads()

Resultados com o Python 3.13 normal#

Resultados com o Python 3.13 normal

Resultados com o Python 3.13 No GIL#

Resultados com o Python 3.13 No GIL

Comparando#

Python Com threads Sem Threads
3.13 8.16193 8.50603
3.13t 0.84548 9.50476
3.12 9.20192 8.78016

A versão 3.13t é a NOGIL e a 3.13 a versão normal. Todos os tempos em segundos.

Quando comparamos a velocidade entre a parte “Com threads” do programa entre as versões 3.13t e 3.13, a diferença de desempenho é enorme. Se você abrir o medidor de desempenho do seu computador, deve ver todos os cores sendo usados. Se seu computador for muito rápido, altere a CARGA no programa para ver o circo pegar fogo :-D.

Porém, veja que na versão “Sem Threads”, o interpretador 3.13t foi mais lento que o 3.13 normal.

Observe também que o interpretador 3.12 foi mais lento que o 3.13 na tarefa “Sem Threads” e mais lento que ambos na versão “Com thread”.

Conclusão#

A versão 3.13 é um avanço nos interpretadores Python, não só pela opção de GIL/NOGIL, mas por dar uma esperança a várias bibliotecas e cargas de trabalho que realmente precisavam de threads. Embora os resultados da execução sequencial ainda não sejam melhores que a da versão tradicional, com GIL, as portas para otimizações estão abertas. Precisamos também esperar que as bibliotecas comecem a publicar versões compatíveis com a 3.13 e com suporte ao interpretador Python 3.13 NOGIL.

A versão 3.13 traz também um JIT simples, que pode melhorar muito no futuro e é outra otimização promissora no interpretador. Agora que está na versão principal, com certeza, a comunidade vai enviar mais código para melhorar e deixar o JIT mais rápido. Quem sabem em pouco tempo chegaremos ao nível do pypy.

Além disso, o time de desenvolvedores do Python conseguiu um compromisso entre testar novas funcionalidades para casos especiais, sem quebrar a compatibilidade com a versão padrão, dando mais tempo para que o código mature e seja testado por mais projetos e desenvolvedores.