Performances : boucles vs. itérateurs
Pour déterminer s’il faut utiliser des boucles ou des iterateurs, vous devez savoir quelle implémentation est la plus rapide : la version de la fonction search avec une boucle for explicite ou la version avec des iterateurs.
Nous avons effectue un benchmark en chargeant l’integralite du contenu des Aventures de Sherlock Holmes de Sir Arthur Conan Doyle dans une String et en recherchant le mot the dans le contenu. Voici les résultats du benchmark sur la version de search utilisant la boucle for et la version utilisant les iterateurs :
test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)
test bench_search_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 l’objectif n’est pas de prouver que les deux versions sont equivalentes, mais d’avoir une idee generale de la comparaison de performances entre ces deux implémentations.
Pour un benchmark plus complet, vous devriez tester avec différents textes de différentes tailles comme contents, différents mots et mots de différentes longueurs comme query, et toutes sortes d’autres variations. Le point essentiel est le suivant : les iterateurs, bien qu’étant une abstraction de haut niveau, sont compilés en un code a peu près identique a celui que vous auriez écrit vous-meme a plus bas niveau. Les iterateurs sont l’une des abstractions a cout zero de Rust, ce qui signifie que l’utilisation de l’abstraction n’impose aucun surcout à l’exécution. Cela est analogue à la maniere dont Bjarne Stroustrup, le concepteur et implementeur original du C++, définit le zero-overhead dans sa presentation ETAPS de 2012 intitulee “Foundations of C++” :
En general, les implémentations C++ obeissent au principe du zero-overhead : ce que vous n’utilisez pas, vous ne le payez pas. Et de plus : ce que vous utilisez, vous ne pourriez pas le coder mieux à la main.
Dans de nombreux cas, le code Rust utilisant des iterateurs est compilé en un assembleur identique a celui que vous ecririez à la main. Des optimisations telles que le deroulement de boucles et l’elimination des vérifications de bornes lors de l’accès aux tableaux s’appliquent et rendent le code resultant extremement efficace. Maintenant que vous savez cela, vous pouvez utiliser les iterateurs et les fermetures sans crainte ! Ils donnent au code une apparence de haut niveau sans imposer de penalite de performance à l’exécution.
Résumé
Les fermetures et les iterateurs sont des fonctionnalités de Rust inspirees des idees de la programmation fonctionnelle. Ils contribuent à la capacité de Rust a exprimer clairement des idees de haut niveau avec des performances de bas niveau. Les implémentations des fermetures et des iterateurs sont telles que les performances à l’exécution ne sont pas affectees. Cela fait partie de l’objectif de Rust de fournir des abstractions a cout zero.
Maintenant que nous avons ameliore l’expressivite de notre projet d’E/S, examinons d’autres fonctionnalités de cargo qui nous aideront a partager le projet avec le monde.