13 pontos por GN⁺ 2025-09-30 | 1 comentários | Compartilhar no WhatsApp
  • Este texto explica, com foco em casos práticos, uma tentativa e um projeto para compilar antecipadamente (AOT) código Python puro e transformá-lo em executáveis multiplataforma
  • A ideia central é não criar um novo JIT nem reescrever tudo em C++, mas gerar kernels otimizados por meio de um pipeline de tracing simbólico → IR → geração de código C++ → compilação para múltiplos alvos
  • Usa anotações de tipo do PEP 484 para iniciar a propagação de tipos e geração de código com IA para implementar automaticamente centenas de operadores em C++, cobrindo chamadas amplas de bibliotecas como Numpy, OpenCV e PyTorch
  • Para a mesma função Python, adota uma estratégia de otimização de desempenho empírica, com geração e distribuição em massa de vários caminhos de implementação e escolha da variante mais rápida com base em telemetria medida em uso real
  • O objetivo é fornecer binários pequenos, rápidos e portáveis, sem dependência de contêineres, como unidade de distribuição para rodar em qualquer lugar, de servidores e desktops até mobile e web

Foreword

  • A simplicidade e a produtividade do Python são vantagens, mas há limitações de desempenho e portabilidade em workloads de alta carga
  • Este texto do autor convidado Yusuf Olokoba apresenta um projeto de compilador para gerar executáveis rápidos e portáteis mantendo o Python original
  • É uma abordagem que busca atingir otimização de kernels com um pipeline sem adicionar JIT nem fazer uma reescrita completa em C++

Introduction

  • O objetivo é compilar Python sem modificações de forma totalmente AOT, para que rode sem interpretador, com desempenho próximo de C/C++ e execução em todas as plataformas
  • Diferentemente de tentativas anteriores (Jython, RustPython, Numba, PyTorch, Mojo etc.), a escolha aqui não é substituir a linguagem ou o runtime, mas sim fazer transformação de código e geração de kernels
  • Essas funções Python compiladas já estão em uso em milhares de dispositivos por mês

Containers Are the Wrong Way to Distribute AI

  • Em implantações reais, contêineres trazem payload excessivo — interpretador, pacotes e snapshot do sistema operacional — o que causa atraso na inicialização e restrições de portabilidade
  • A alternativa é um executável autossuficiente contendo apenas o modelo, oferecendo tamanho menor, inicialização mais rápida e possibilidade de execução em servidor, desktop, mobile e web
  • A ideia central é mudar a unidade de distribuição de um snapshot de SO para um binário autoexecutável

Arm64, Apple, and Unity: How It All Began

  • Durante a transição da Apple para arm64, o caso do Unity, que convertia CIL em C++ com IL2CPP para permitir compilação para todos os alvos, serviu como benchmark
  • A visão foi aplicar a mesma ideia ao Python para garantir um caminho de código que possa rodar em qualquer lugar

Sketching Out a Python Compiler

  • O desenho de alto nível é composto pelas etapas entrada em Python → tracing simbólico (IR) → geração em C++ → compilação multi-target
  • O motivo para não ir direto do IR ao código-objeto e escolher C++ como artefato intermediário é aproveitar ao máximo caminhos de aceleração como CUDA, MLX, TensorRT, AMX e outros
  • O objetivo é garantir um projeto extensível no qual seja fácil encaixar caminhos ótimos específicos para cada hardware

Building a Symbolic Tracer for Python

  • No início, o tracing com base em PyTorch FX tinha limitações por exigir execução e por ficar restrito a operações do PyTorch
  • Em vez disso, foi construído um tracer simbólico baseado em parsing de AST, que converte fluxo de controle e resolução de chamadas em IR
  • Hoje, o tracer oferece recursos como análise estática, avaliação parcial e observação de valores em tempo de execução com sandbox

Lowering to C++ via Type Propagation

  • Para fazer a ponte entre a tipagem dinâmica do Python e a tipagem estática do C++, usa-se propagação de tipos
  • Quando os tipos dos argumentos de entrada são fornecidos, os tipos das variáveis intermediárias podem ser inferidos de forma determinística com base nas definições dos operadores
  • Cada operação em Python é mapeada para sua implementação correspondente em C++, com os tipos sendo propagados por toda a função

Seeding the Type Propagation Process

  • Como ponto de partida da propagação de tipos, são usadas anotações de tipo do PEP 484
  • Isso entra em conflito com o princípio de não modificar o código original, mas foi considerado um compromisso aceitável em nome de uma interface concisa e compatibilidade
  • Também são impostas restrições, como limites no número de tipos na assinatura da função, para garantir uma interface de consumo simples

Building a Library of C++ Operators

  • Não é necessário implementar todas as funções diretamente em C++; apenas as operações folha que não podem ser rastreadas exigem implementação manual ou automática
  • Como muito código Python é composto por combinações de poucas operações básicas, o conjunto de operadores a cobrir é relativamente pequeno
  • Com geração de código baseada em LLM e uma infraestrutura de restrições, testes e compilação condicional, foi automatizada a implementação de centenas de funções de Numpy, OpenCV, PyTorch e outras bibliotecas

Performance Optimization via Exhaustive Search

  • Partindo da lição de que a otimização de desempenho é sempre empírica, a estratégia é gerar todas as variantes de implementação possíveis e escolher a melhor por comparação com medições reais
  • Exemplo: no Apple Silicon, até mesmo para resize são gerados vários caminhos, como Accelerate, vImage, Core Image e Metal, com múltiplos binários da mesma funcionalidade sendo distribuídos
  • Com telemetria detalhada, coleta-se a latência por caminho, e um modelo estatístico prevê e seleciona a variante mais rápida
  • Na prática, isso oferece ao usuário uma experiência de execução que fica automaticamente mais rápida com o tempo

Designing a User Interface for the Compiler

  • Para manter a experiência do desenvolvedor com curva de aprendizado próxima de zero, foi adotado como interface o decorator PEP 318 @compile
  • A CLI usa o decorator como entrypoint para percorrer e compilar o grafo de código dependente
  • Os argumentos do decorator incluem tag, description, sandbox e metadata, com suporte a reprodução de ambiente e especificação de backend (ONNXRuntime, TensorRT, CoreML, IREE, QNN etc.)

Closing Thoughts

  • Recursos como exceções, lambdas, recursão e classes têm suporte parcial ou inexistente, e especialmente em tipos compostos e tipos de ordem superior ainda é necessário expandir a propagação de tipos
  • A experiência de depuração também é um desafio, já que a compilação otimizada reduz as informações simbólicas e torna o rastreamento mais difícil
  • std::span, concepts e coroutines do C++20 são bases importantes, enquanto std::generator, <stdfloat> e <stacktrace> do C++23 devem contribuir para streaming, half/bfloat16 e rastreamento de exceções
  • O objetivo final é estabelecer, sem contêineres, um executável pequeno, rápido e seguro como unidade de distribuição capaz de rodar workloads de IA, como embedding e detecção, em qualquer lugar

1 comentários

 
secret3056 2025-09-30

Achei que fosse algo como APE, mas não é.