- A comparação de desempenho entre Rust e C é uma questão complexa que depende de como se define a premissa de que “todas as condições são iguais”
- No caso de assembly inline, ambas as linguagens podem gerar o mesmo código assembly, portanto não há diferença de velocidade da linguagem em si
- Em layout de memória de structs, Rust pode ter tamanho menor ao reordenar campos, mas também pode usar o atributo
#[repr(C)] para ter o mesmo layout de C
- Devido a diferenças em verificações em tempo de execução e no comportamento dos desenvolvedores, a estrutura do código e o desempenho podem variar em projetos reais
- Em conclusão, não há diferença de desempenho causada por limitações da linguagem em si, e o resultado varia conforme o projeto e fatores ligados aos desenvolvedores
Formulação do problema e ambiguidade das premissas
- O ponto de partida é a pergunta levantada no Reddit: “se as condições forem as mesmas, Rust pode ser mais rápido que C?”
- A própria expressão “todas as condições são iguais” é um conceito muito difícil de definir em comparações entre linguagens
- A comparação de desempenho depende não só das diferenças entre linguagens, mas também da forma do código, das decisões de desenvolvimento e das otimizações do compilador
Comparação de assembly inline
- Rust oferece suporte a assembly inline no nível da linguagem, enquanto C implementa isso por meio de extensões do compilador
- Em ambas as linguagens, é possível escrever o mesmo exemplo usando a instrução
rdtsc
- O assembly gerado por
rustc 1.87.0 e clang 20.1.0 sai em formato completamente idêntico
- Esse caso não demonstra uma diferença de desempenho entre linguagens, mas comprova que Rust pode realizar controle de baixo nível no mesmo nível que C
Diferenças no layout de structs
- Structs em Rust podem otimizar o uso de memória por meio da reordenação de campos
- No exemplo, a struct em Rust tem 16 bytes, enquanto a mesma struct em C totaliza 24 bytes
- Em C, é preciso alterar manualmente a ordem dos campos para obter o mesmo tamanho
- Em Rust, ao usar o atributo
#[repr(C)], é possível forçar o mesmo layout de memória de C
Fatores sociais e dos desenvolvedores
- Graças às verificações de segurança do Rust, há casos em que desenvolvedores conseguem tentar otimizações mais agressivas
- No projeto Stylo da Mozilla, duas tentativas de paralelização em C++ fracassaram, mas em Rust a implementação foi bem-sucedida
- Mesmo em um mesmo projeto, o desempenho e a estabilidade do código resultante podem variar conforme a linguagem e o nível de experiência dos desenvolvedores
- Como o resultado de “fazer a mesma tarefa” muda entre iniciantes, especialistas e diferentes níveis de domínio da linguagem, uma comparação simples é difícil
Verificações em tempo de compilação e em tempo de execução
- Muitas verificações de segurança do Rust são feitas em tempo de compilação, mas algumas permanecem como verificações em tempo de execução
- Por exemplo, ao acessar
array[0], Rust faz verificação de limites, enquanto C não faz
- Em Rust, ao usar
get_unchecked(), é possível obter o mesmo comportamento de C
- Quando o compilador consegue provar a segurança, ambas as linguagens podem eliminar essas verificações por otimização
- Essas diferenças afetam a forma como o código é escrito e, como consequência, podem gerar diferenças de desempenho
Conclusão
- Mesmo assumindo que C seja “a linguagem mais rápida”, não há motivo para que Rust não possa atingir o mesmo nível de desempenho
- Mais do que limitações da linguagem em si, características do projeto, capacidade dos desenvolvedores e restrições de tempo determinam as diferenças de desempenho
- Portanto, a pergunta “Rust é mais rápido que C?” deve ser interpretada mais como uma questão de contexto de engenharia do que de comparação entre linguagens
9 comentários
Opiniões do Hacker News
Resumindo, a velocidade máxima é quase a mesma, mas no código real a diferença é grande
em especial, o multithreading é uma variável importante. No Rust, todas as variáveis globais precisam ser thread-safe, usem threads ou não, e o borrow checker limita o acesso à memória a compartilhamento ou modificação
por isso, no Rust, escrever código multithread é quase o padrão. Já em C, criar threads por si só pesa por questões de compatibilidade entre plataformas e riscos de depuração
Em C, criar threads não é difícil, mas é mais trabalhoso do que no Rust com
std::thread::spawn(move || { ... });Mais do que segurança de memória, o modelo de concorrência da linguagem tem maior impacto. O Go paraleliza facilmente com
go f()mesmo sem ser memory-safePessoalmente, vi heisenbugs com mais frequência em Go
#pragma omp forjá permite paralelizar com facilidade também em CGraças aos traits do Rust, dá para criar abstrações mais rápidas e flexíveis
Em C, isso pode ser imitado com macros ou ponteiros de função, mas no Rust quem chama pode escolher entre despacho dinâmico e despacho estático
Em ambientes embarcados, ponteiros de função prejudicam o cache e reduzem o desempenho, enquanto os traits do Rust permitem otimização por inlining e são bem mais eficientes
No fim, seja em Rust ou em C, tudo acaba sendo tratado em bytes, e hoje em dia as ferramentas de binary patching também melhoraram bastante, então são fáceis de aproveitar
Box<dyn Trait>na assinatura da função, quem chama fica forçado ao despacho dinâmicoCom
impl Trait, a escolha continua com quem chamaPessoalmente, vejo Rust, C e C++ quase como a mesma família de linguagens de baixo nível, então a diferença de desempenho é mínima
As regras estritas de aliasing do Rust ajudam na otimização, e o UB (comportamento indefinido) de C/C++ existe em parte por desempenho
Além disso, os genéricos de Rust e C++ são muito mais poderosos do que em C, então, por exemplo, uma ordenação baseada em template é muito mais fácil de otimizar com inline do que
qsort()Acho que essas discussões sobre velocidade entre linguagens são, em grande parte, inúteis
Mais do que a linguagem em si, é a implementação do compilador que determina o desempenho
Rust, C e C++ são todos linguagens de baixo nível, mas é importante definir o que significa “rápido”
Estamos falando da velocidade máxima de código otimizado por especialistas, ou da probabilidade de um desenvolvedor mediano escrever código rápido dentro do orçamento?
Mas, com otimização manual, a diferença entre as linguagens praticamente desaparece
Ainda assim, o Rust tem uma leve vantagem por ser uma linguagem em que é mais fácil escrever código rápido
Eu achava que as vantagens do Rust eram multithreading e alocação na stack
Graças ao modelo de ownership, dá para colocar mais coisas na stack do que em C/C++, reduzindo o overhead de malloc/free
Esse tipo de tema costuma gerar discussões emocionais, então eu queria tratar mais das diferenças de modo de pensar do que de números concretos
Ao discutir a “velocidade” de uma linguagem, é preciso olhar para duas coisas
Rust e C quase não têm checks em tempo de execução, então são mais rápidos que Python ou JS
Ainda assim, o Rust transmite melhor as informações de aliasing, o que abre mais espaço para otimização
No modo debug, ele pode ser tão lento quanto Ruby, mas no modo release chega a velocidades de nível C
Em comparação com C, C++ e Rust têm mais recursos de tempo de compilação, então fica mais fácil escrever código rápido
Por exemplo, este código é praticamente impossível em C
Em C, seriam necessárias ferramentas externas como re2c
Como assembly não faz parte do padrão C, é difícil compará-lo diretamente com Rust, e o Rust tem, na verdade, um perfil mais próximo do projeto GCC
No fim, se uma linguagem é “rápida” depende de implementação e contexto
Mais do que da velocidade da linguagem em si, o impacto maior vem da combinação entre compilador e hardware
Em média, não sei qual linguagem seria a mais rápida, mas acho que a dispersão no C++ deve ser a maior.
Em sistemas embarcados, a gente programa levando em conta até o tamanho da linha de cache do hardware. No fim, a questão é até onde o programador consegue levar a otimização extrema na linguagem e também o desempenho da biblioteca padrão e do compilador; como ambos oferecem suporte a baixo nível, parece que a diferença de overhead entre os dois deve ser mínima. Então não parece ser uma discussão tão significativa... Se for necessária uma otimização extrema, no fim das contas a intervenção humana acaba sendo necessária. Os compiladores não são tão perfeitos quanto se imagina.
Acho que Rust vai acabar sendo mais um substituto de C++ do que de C. C é praticamente a única (talvez a última) linguagem em que dá para imaginar o código que o compilador vai gerar…
O Zig também não é ruim... T_T
Acabei escrevendo num estilo de resumo de IA T_T
Você não fez isso de propósito, né? rs
Isso depende da capacidade do compilador.
Se montar o mesmo código em assembly, dá para ver o resultado.
Parece que o pessoal do ffmpeg acha que Rust não é mais rápido que C mesmo kkk https://www.memorysafety.org/blog/rav1d-perf-bounty/