Que fait Blazing Rust ?
Améliorez les performances mesurées tout en préservant le comportement. Gardez le changement le plus simple qui produit un gain reproductible sur des entrées représentatives.
Mesurer avant de modifier
- Identifiez la charge de travail, la métrique cible et les contraintes de correction. Lisez le chemin chaud et ses appelants. Respectez la chaîne d'outils du projet, la version minimale de Rust supportée, les fonctionnalités et les cibles de déploiement. Une demande d'explication autorise la mesure et le profilage, pas les modifications.
- Capturez une référence avec le profil de production optimisé du projet, généralement
release. Enregistrez l'entrée, la commande exacte, la chaîne d'outils, les drapeaux, le CPU et le nombre de threads. Utilisez des tailles d'entrée et des distributions réalistes, y compris les cas petits et défavorables. - Trouvez ce qui limite les performances. Profilez le temps CPU, les allocations, les E/S ou les attentes de verrou selon le cas. Pour les blocages asynchrones, inspectez l'ordonnancement des tâches et la croissance des files d'attente avec un traçage sensible au runtime. Utilisez d'abord les outils existants ; la section de profilage liste les outils courants et comment conserver les symboles dans les versions release. Si le profilage n'est pas disponible, utilisez des mesures ciblées et indiquez les limites des preuves.
- Énoncez une hypothèse et effectuez un changement mesurable. Exécutez des tests de correction ciblés et répétez le même benchmark. Vérifiez le résultat dans la charge de travail complète avant de revendiquer une amélioration visible par l'utilisateur.
- Conservez les gains justifiés. Annulez uniquement vos modifications infructueuses, en préservant le travail non lié. Arrêtez-vous lorsque l'objectif est atteint ou que les changements supplémentaires manquent de preuves.
Rendre le benchmark crédible
- Séparez la configuration, la compilation, la génération d'entrée et le démarrage du processus de l'opération, sauf s'ils font partie de la métrique cible. Consommez les résultats. Pour les microbenchmarks, utilisez les barrières d'optimisation de la bibliothèque de benchmark ou
std::hint::black_boxsur les entrées et sorties. Ces barrières sont au mieux non garanties, alors inspectez les résultats invraisemblables. - Comparez un travail et des résultats équivalents. Gardez la chaîne d'outils, le profil, les fonctionnalités, l'allocateur, les paramètres CPU et le nombre de threads fixes, sauf si l'un d'eux est la variable testée. Échauffez-vous lorsque vous mesurez l'état stable. Mesurez les démarrages à froid séparément lorsque cela est pertinent.
- Répétez les exécutions et rapportez le nombre d'échantillons, la médiane et la variabilité. Alternez les exécutions de référence et candidates lorsque la dérive est importante. Une différence dans le bruit n'établit pas un gain.
- Adaptez les métriques à la tâche. Pour les services, mesurez la latence p95/p99 à une charge donnée, le débit, les erreurs et l'utilisation des ressources. Vérifiez que la latence réduite ne provient pas d'un travail abandonné ou d'une file d'attente croissante.
- Réutilisez les benchmarks existants. Ajoutez un petit cas de régression lorsque la nouvelle logique en nécessite un. N'installez pas d'outils, n'ajoutez pas de dépendances et ne modifiez pas les paramètres de construction sans l'autorisation de la tâche. N'inventez jamais de mesures.
Choisir les changements à partir des preuves
Commencez par le travail inutile et les algorithmes. Envisagez des modifications de plus bas niveau uniquement lorsque le goulot d'étranglement restant le justifie. Lisez la section correspondante du guide des techniques lorsqu'un goulot d'étranglement mesuré nécessite plus de détails.
| Coût observé | Changement candidat et contrôles |
|---|---|
| Parcours ou calculs répétés | Améliorez l'algorithme ou le choix de collection. Supprimez les analyses, tris, sérialisations et recherches en double. Tenez compte de l'invalidation de cache et de la mémoire si la mise en cache aide. |
| Allocation ou copie | Empruntez &str et des slices, déplacez les valeurs possédées, réutilisez les tampons et réservez des capacités réalistes. Essayez clone_from lors du remplacement d'une valeur allouée. Évitez les collections intermédiaires. clone peut être peu coûteux pour les handles partagés, alors mesurez son coût. |
| Recherches dans les collections | Utilisez des clés empruntées et des API entry lorsqu'elles évitent un travail répété. Comparez des vecteurs denses ou des données triées lorsque le modèle d'accès le permet. Préservez la résistance aux attaques par collision pour les clés non fiables lors du changement de hasher. |
| Tri ou suppression | Utilisez select_nth_unstable pour la sélection, sort_unstable lorsque l'ordre des éléments égaux est sans importance, et sort_by_cached_key pour les clés coûteuses. Utilisez swap_remove uniquement lorsque l'ordre des éléments est sans importance. |
| Miss de cache ou grandes valeurs | Comparez des tableaux de structures avec des structures de tableaux. Vérifiez size_of, align_of et les misses de cache. Le stockage en ligne peut augmenter la copie. Envisagez de mettre dans une Boîte les charges utiles rares et volumineuses d'une enum, en tenant compte des allocations et de l'indirection. |
| Petits appels ou boucles chauds | Préférez l'itération ordinaire. Aidez le compilateur à supprimer les vérifications de limites avec des slices, l'itération ou des assertions de plage. Inspectez l'assembly optimisé avant l'inlining ou le déroulage. Testez les entrées vides, les restes, les débordements et la taille du code. |
| Recherche de texte | Réutilisez les expressions rationnelles compilées et ne demandez que les informations de correspondance nécessaires. Comparez des recherches d'octets spécialisées lorsque la sémantique d'encodage le permet. Évitez de reconstruire l'état de recherche à l'intérieur des boucles chaudes. |
| Calcul vectorisable | Vérifiez l'auto-vectorisation avant le SIMD explicite. Inspectez le code généré. Protégez les instructions spécifiques à la cible si nécessaire et conservez une solution de repli scalaire. Testez les restes et les CPU représentatifs. Préservez l'alignement, les limites et le comportement numérique. |
| Travail CPU indépendant | Comparez l'exécution séquentielle avec le parallélisme borné. Mesurez le coût d'ordonnancement, la contention, la bande passante, le nombre de threads et la consommation CPU totale. Ajustez la taille des tâches uniquement lorsque la surcharge est importante. Préservez la sémantique de réduction. |
| E/S ou blocages asynchrones | Réutilisez les tampons de ligne, regroupez les petites E/S et gérez les erreurs de vidage. Préservez la latence, la gestion des nouvelles lignes et la durabilité. Gardez le travail bloquant ou CPU lourd hors des workers asynchrones. Limitez la concurrence et les files d'attente. Préservez l'ordre, la contre-pression et l'annulation. |
| Limites de construction ou de génération de code | Comparez les paramètres existants avec thin LTO. Testez fat LTO et codegen-units = 1 séparément avant de les combiner. Incluez le temps de build et la taille du binaire. Utilisez des drapeaux spécifiques au CPU uniquement lorsque le matériel de déploiement le permet. |
Préserver la correction
Conservez intacts les API publiques, la gestion des erreurs, la validation, l'ordre, la sérialisation et les contrats FFI. Préservez le comportement de débordement d'entiers et de nombres à virgule flottante. La représentation d'une enum ou l'ordre de ses variantes peut modifier la disposition et les discriminants ; rien ne garantit une enum plus petite ou plus rapide.
Préférez Rust sécurisé et les dépendances existantes. L'introduction de code non sécurisé nécessite un besoin mesuré, des invariants de sécurité explicites et des tests ciblés. Utilisez Miri ou les sanitizers lorsque c'est pris en charge et pertinent. Ne sacrifiez jamais la validation ou la fiabilité pour la vitesse du benchmark, et ne traitez pas les tests réussis comme une preuve de fiabilité.
Rapporter le résultat
Indiquez le goulot d'étranglement, le changement et la décision de conserver ou de revenir en arrière. Incluez des commandes reproductibles, les résultats avant/après avec variabilité, les chiffres d'allocation ou de mémoire pertinents, les contrôles de correction et les compromis. Distinguez les gains de temps horloge multicœur de l'efficacité monocœur. Nommez les affirmations non mesurées et les plateformes non vérifiées.