Programação e desenvolvimento de software

Como integrar Rust gradualmente para acelerar uma base de código existente sem reescrevê-la por completo

Lily Mara explica uma metodologia de refatoração de funções por meio da Interface de Funções Estrangeiras (FFI) para transferir partes específicas de Python para Rust usando PyO3, mantendo a aplicação existente e reduzindo os riscos de uma reescrita abrangente. A experiência mostra que o ganho de velocidade no nível da função pode ultrapassar 100 vezes, enquanto a melhoria no teste HTTP completo chegou a apenas cerca de 15%, destacando a importância de medir no nível do sistema.

2026-09-15
6 min de leitura
10 visualizações
فريق تحرير certi.news
Como integrar Rust gradualmente para acelerar uma base de código existente sem reescrevê-la por completo

Em vez de reescrever uma aplicação inteira em Rust, Lily Mara, Staff Engineer no Discord, propõe transferir gradualmente as funções que mais consomem recursos de uma linguagem dinâmica como Python para Rust, conectando as duas implementações dentro do mesmo processo. Mara apresentou essa metodologia em uma sessão publicada pela InfoQ, com base em sua experiência na construção de sistemas distribuídos para enviar dezenas de bilhões de notificações diariamente aos usuários do Discord e em seu livro Refactoring to Rust.

A ideia principal não é substituir uma linguagem por outra apenas porque Rust é mais rápido, mas identificar partes restritas do sistema que produzam o maior impacto quando aprimoradas. Essa abordagem permite reduzir o escopo da mudança, testar o novo comportamento em comparação com o antigo e manter a implementação original nos casos em que é difícil garantir resultados idênticos.

Por que uma reescrita completa não deve ser o ponto de partida?

Mara considera compreensível o entusiasmo por reconstruir completamente um sistema antigo em uma linguagem mais moderna, mas isso envolve grandes riscos práticos. Projetos de reescrita podem ultrapassar os prazos, tornar-se mais complexos do que o previsto ou reintroduzir erros que o sistema antigo já havia resolvido. Além disso, o código legado nem sempre é complexo por causa de sua idade; ele pode refletir restrições e detalhes acumulados a partir do uso real e de conhecimento institucional difícil de transferir para um projeto novo.

A sessão também alerta contra reduzir o problema de desempenho à linguagem de programação. A causa da lentidão pode estar no esquema do banco de dados, nos padrões de consulta e cache ou no design dos serviços, e não no custo de execução da linha de código. Portanto, aproveitar Rust não significa que a reescrita corrigirá automaticamente os gargalos arquiteturais.

O que significa refatoração por FFI?

A metodologia usa o que Mara chama de FFI refactoring, ou seja, reescrever uma função ou uma pequena parte das funções em outra linguagem e conectá-la à aplicação existente por meio da Interface de Funções Estrangeiras. No exemplo apresentado, a aplicação Flask continua recebendo a solicitação HTTP e decodificando o JSON, depois envia os dados para uma função estatística escrita em Rust, antes de retornar os resultados ao Python e enviar a resposta.

A conexão baseia-se na interface C, que funciona na prática como uma linguagem comum entre muitos sistemas e linguagens. No caso do Python, o projeto PyO3 fornece ferramentas para criar um módulo importável pelo Python, enquanto o Maturin pode ser usado para compilar esse módulo. Atributos de programação como pyfunction e pyclass transformam funções e estruturas de dados em Rust em interfaces que podem ser chamadas pelo Python.

Escolher a função certa e medir o impacto

A experiência recomenda procurar funções que combinem alta frequência de chamadas ou alto custo por chamada. A função pode ser relativamente barata a cada execução, mas ser executada em todas as solicitações, como a lógica de validação presente diante dos manipuladores de API. Por outro lado, pode haver operações raras, mas muito dispendiosas. O critério é o impacto acumulado no tempo de CPU, não a impressão de que determinada função parece lenta.

No exemplo estatístico, a implementação em Rust foi pouco mais de cem vezes mais rápida quando a própria função foi medida por meio do Python, embora o teste tenha executado as duas aplicações pelo interpretador Python. Mas, ao medir um manipulador HTTP completo, incluindo Flask, a serialização e a desserialização de JSON, a melhoria foi de apenas cerca de 15%. O tempo de execução da versão original da função foi de aproximadamente 86 microssegundos, um tempo pequeno isoladamente, mas que pode se transformar em um custo relevante quando repetido em grande escala.

Essa diferença entre a medição parcial e a medição total é uma das principais lições da sessão: não basta registrar a aceleração da função; é necessário realizar testes completos que representem o fluxo real de uso e incluam a rede ou a estrutura web e a serialização de dados quando necessário.

Compatibilidade funcional, testes e limitações

A reimplementação do exemplo revelou uma diferença entre os resultados da biblioteca estatística em Python e da biblioteca em Rust. Uma das bibliotecas calculou os quartis com precisão, enquanto a outra usou estimativas adequadas para conjuntos enormes; também surgiram pequenas diferenças no arredondamento decimal. Portanto, não se deve presumir que substituir a biblioteca preserve automaticamente o mesmo comportamento.

Se a equivalência for necessária, é possível procurar outra biblioteca, reimplementar o algoritmo original em Rust ou manter a parte sensível em Python. A vantagem de transferir a implementação no nível da função é permitir a combinação das duas linguagens dentro do mesmo processo, em vez de separar o componente em um serviço independente e acrescentar comunicações de rede e novos custos operacionais.

Quanto aos testes, eles incluem testes diretos em Rust, os testes existentes em Python para os manipuladores do Flask, testes que comparam os resultados das duas implementações e, quando apropriado, testes aleatórios de propriedades. Ainda assim, adicionar código nativo impõe complexidades operacionais: ambientes de desenvolvimento precisam de um compilador Rust ou de binários compatíveis com o sistema operacional e a arquitetura, o processo de implantação se torna mais complexo e podem surgir novos erros.

Na prática, essa metodologia oferece um caminho relativamente de baixo risco para melhorar partes selecionadas de sistemas existentes, mas não elimina a necessidade de análise arquitetural, testes de compatibilidade e medições completas. A economia real só fica comprovada quando a melhoria se reflete no fluxo completo do serviço, e não em um benchmark isolado da função.

Fonte da notícia
InfoQ - Architecture Articles
Abrir fonte original ↗
ف
Autor

فريق تحرير certi.news

Na mesma categoria

Você também pode gostar

Ver todas as notícias