Comparaison des performances : les boucles et les itérateurs
Pour déterminer s’il faut utiliser des boucles ou des itérateurs, nous devons savoir quelle implémentation est la plus rapide : la version de la fonction rechercher avec une boucle for explicite, ou la version avec des itérateurs.
Nous avons lancé un benchmark en chargeant tout le contenu de The Adventures of Sherlock Holmes de Sir Arthur Conan Doyle dans une String et en cherchant le mot “the” dans le contenu. Voici les résultats du benchmark sur la version de rechercher avec une boucle for et avec un itérateur :
test bench_rechercher_for ... bench: 19,620,300 ns/iter (+/- 915,700)
test bench_rechercher_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
Les deux implémentations ont des performances similaires ! Nous n’expliquerons pas le code du benchmark ici, car il ne s’agit pas de prouver que les deux versions sont équivalentes, mais d’avoir une idée générale de la différence de performances entre les deux.
Pour un benchmark plus complet, nous vous conseillons d’utiliser des textes de différentes tailles pour contenu, des mots différents et de différentes longueurs pour recherche, ainsi que tout autre type de variation que vous pourriez trouver. Le point important est le suivant : les itérateurs, bien qu’il s’agisse d’une abstraction de haut niveau, sont compilés à peu près comme si vous aviez écrit vous-même le code un niveau plus bas. Les itérateurs sont l’une des abstractions à coût zéro de Rust, c’est-à-dire que l’utilisation de l’abstraction n’impose aucun surcoût lors de l’exécution. C’est la même notion que celle que Bjarne Stroustrup, le concepteur et développeur original de C++, définit en tant que coût zéro dans son discours d’ouverture à l’ETAPS de 2012 intitulé “Foundations of C++” :
En général, les implémentations de C++ obéissent au principe du coût zéro : ce que vous n’utilisez pas, vous ne le payez pas. Et plus encore : ce que vous utilisez, vous ne pourrez pas le coder mieux à la main.
Dans de nombreux cas, le code Rust utilisant des itérateurs se compile en un code assembleur identiques à celui que vous écririez à la main. Des optimisations telles que le déroulement des boucles et la suppression des vérifications des bornes lors des accès aux tableaux s’appliquent, ce qui rend le code produit extrêmement efficace. Maintenant que vous savez cela, vous pouvez utiliser des itérateurs et des fermetures sans crainte ! Ils font en sorte que le code paraisse de haut niveau, mais n’entraînent pas de pénalité de performance à l’exécution.
Résumé
Les fermetures et les itérateurs sont des fonctionnalités de Rust inspirées par des idées des langages de programmation fonctionnels. Ils contribuent à la capacité de Rust d’exprimer clairement des idées de haut niveau avec des performances dignes d’un langage de bas niveau. Les implémentations des fermetures et des itérateurs sont telles que les performances à l’exécution n’en sont pas affectées. Cela fait partie de l’objectif de Rust de s’efforcer à fournir des abstractions à coût zéro.
Maintenant que nous avons amélioré l’expressivité de notre projet d’entrée/sortie, regardons d’autres fonctionnalités fournies par cargo qui nous aideront à partager notre projet avec le monde entier.