Rust Turbinado
Melhore o desempenho medido preservando o comportamento. Mantenha a mudança mais simples que produz um ganho reproduzível em entradas representativas.
Meça antes de editar
- Identifique a carga de trabalho, a métrica alvo e as restrições de corretude. Leia o caminho crítico e seus chamadores. Respeite a toolchain do projeto, a versão mínima suportada do Rust, as features e os alvos de implantação. Uma solicitação de explicação autoriza medição e perfilamento, não edições.
- Capture uma linha de base com o perfil de produção otimizado do projeto, geralmente
release. Registre a entrada, o comando exato, a toolchain, as flags, a CPU e o número de threads. Use tamanhos e distribuições de entrada realistas, incluindo casos pequenos e adversos. - Descubra o que limita o desempenho. Faça perfilamento de tempo de CPU, alocações, I/O ou esperas de bloqueio, conforme apropriado. Para stalls assíncronos, inspecione o escalonamento de tarefas e o crescimento de filas com rastreamento ciente do runtime. Use ferramentas existentes primeiro; a seção de perfilamento lista as comuns e como manter símbolos em builds de release. Se o perfilamento não estiver disponível, use medições direcionadas e declare os limites das evidências.
- Enuncie uma hipótese e faça uma mudança mensurável. Execute testes de corretude focados e repita o mesmo benchmark. Verifique o resultado na carga de trabalho completa antes de reivindicar uma melhoria perceptível pelo usuário.
- Mantenha ganhos suportados. Desfaça apenas suas edições malsucedidas, preservando o trabalho não relacionado. Pare quando a meta for atingida ou quando mudanças adicionais carecerem de evidências.
Torne o benchmark crível
- Separe a configuração, compilação, geração de entrada e inicialização do processo da operação, a menos que eles pertençam à métrica alvo. Consuma os resultados. Para microbenchmarks, use as barreiras de otimização da biblioteca de benchmarks ou
std::hint::black_boxnas entradas e saídas. Essas barreiras são de melhor esforço, portanto inspecione resultados implausíveis. - Compare trabalho e saídas equivalentes. Mantenha a toolchain, perfil, features, alocador, configurações de CPU e contagem de threads fixos, a menos que um deles seja a variável em teste. Aqueça ao medir estado estacionário. Meça partidas a frio separadamente quando relevante.
- Repita as execuções e relate o número de amostras, mediana e variabilidade. Alterne execuções de linha de base e candidatas quando a deriva importar. Uma diferença dentro do ruído não estabelece uma vitória.
- Combine métricas à tarefa. Para serviços, meça latência p95/p99 em uma carga declarada, throughput, erros e uso de recursos. Verifique se a latência reduzida não veio de trabalho descartado ou de uma fila crescente.
- Reutilize benchmarks existentes. Adicione um pequeno caso de regressão quando uma nova lógica precisar de um. Não instale ferramentas, adicione dependências ou altere configurações de build sem a autorização da tarefa. Nunca invente medições.
Escolha mudanças com base em evidências
Comece com trabalho desnecessário e algoritmos. Considere mudanças de nível mais baixo apenas quando o gargalo restante as exigir. Leia a seção correspondente de o guia de técnicas quando um gargalo medido precisar de mais detalhes.
| Custo observado | Mudança candidata e verificações |
|---|---|
| Varreduras ou computação repetidas | Melhore o algoritmo ou a escolha da coleção. Remova parsing, ordenação, serialização e buscas duplicados. Considere invalidação de cache e memória se o cache ajudar. |
| Alocação ou cópia | Tome emprestado &str e slices, mova valores ownership, reutilize buffers e reserve capacidades críveis. Tente clone_from ao substituir um valor alocado. Evite coleções intermediárias. clone pode ser barato para handles compartilhados, então meça seu custo. |
| Buscas em coleções | Use chaves emprestadas e APIs de entrada onde elas evitam trabalho repetido. Compare vetores densos ou dados ordenados quando o padrão de acesso se adequar. Preserve a resistência a ataques de colisão para chaves não confiáveis ao alterar hashers. |
| Ordenação ou remoção | Use select_nth_unstable para seleção, sort_unstable quando a ordem de elementos iguais for irrelevante e sort_by_cached_key para chaves caras. Use swap_remove apenas quando a ordem dos elementos for irrelevante. |
| Erros de cache ou valores grandes | Compare arrays de estruturas com estruturas de arrays. Verifique size_of, align_of e erros de cache. Armazenamento inline pode aumentar a cópia. Considere encaixotar payloads grandes de enums raros, contabilizando alocações e indireção. |
| Pequenas chamadas ou loops críticos | Prefira iteração comum. Ajude o compilador a remover verificações de limites com slices, iteração ou asserções de intervalo. Inspecione o assembly otimizado antes de inlining ou unrolling. Teste entradas vazias, caudas, estouro e tamanho do código. |
| Busca de texto | Reutilize regexes compilados e solicite apenas as informações de correspondência necessárias. Compare buscas especializadas de bytes quando a semântica de codificação permitir. Evite reconstruir o estado de busca dentro de loops críticos. |
| Computação vetorizável | Verifique a autovetorização antes de SIMD explícito. Inspecione o código gerado. Proteja instruções específicas de destino quando necessário e mantenha um fallback escalar. Teste caudas e CPUs representativas. Preserve alinhamento, limites e comportamento numérico. |
| Trabalho independente de CPU | Compare execução serial com paralelismo limitado. Meça custo de escalonamento, contenção, largura de banda, contagem de threads e consumo total de CPU. Ajuste o tamanho da tarefa apenas quando a sobrecarga importar. Preserve semântica de redução. |
| I/O ou stalls assíncronos | Reutilize buffers de linha, faça lote de pequenas E/S e trate erros de flush. Preserve latência, manipulação de nova linha e durabilidade. Mantenha trabalho bloqueante ou pesado de CPU fora dos workers assíncronos. Limite concorrência e filas. Preserve ordenação, contrapressão e cancelamento. |
| Limites de build ou geração de código | Compare configurações existentes com thin LTO. Teste fat LTO e codegen-units = 1 separadamente antes de combinar. Inclua tempo de build e tamanho do binário. Use flags específicas de CPU apenas quando o hardware de implantação as permitir. |
Preserve a corretude
Mantenha APIs públicas, tratamento de erros, validação, ordenação, serialização e contratos FFI intactos. Preserve o comportamento de estouro de inteiros e ponto flutuante. A representação de enum ou a ordem das variantes pode alterar layout e discriminantes; nenhum deles garante uma enum menor ou mais rápida.
Prefira Rust seguro e dependências existentes. Introduzir código inseguro requer uma necessidade medida, invariantes de segurança explícitos e testes focados. Use Miri ou sanitizadores onde suportado e relevante. Nunca troque validação ou solidez por velocidade de benchmark, e não trate testes bem-sucedidos como prova de solidez.
Relate o resultado
Declare o gargalo, a mudança e a decisão de manter ou reverter. Inclua comandos reproduzíveis, resultados antes/depois com variabilidade, figuras relevantes de alocação ou memória, verificações de corretude e trade-offs. Diferencie ganhos de wall-clock multicore de eficiência single-core. Nomeie reivindicações não medidas e plataformas não verificadas.