ループとイテレータにおけるパフォーマンス
ループとイテレータのどちらを使うべきかを判断するには、どちらの
実装がより高速なのかを知る必要があります。つまり、明示的な
for ループを使った search 関数のバージョンと、イテレータを使った
バージョンのどちらが速いかです。
私たちは、アーサー・コナン・ドイル卿の シャーロック・ホームズの冒険 の
全文を String に読み込み、その内容の中から単語 the を探すことで
ベンチマークを実行しました。for ループを使う search のバージョンと、
イテレータを使うバージョンでのベンチマーク結果は次のとおりです。
test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
2 つの実装のパフォーマンスはほぼ同じです!ここではベンチマーク コードの説明はしません。というのも、要点は 2 つのバージョンが等価で あることを証明することではなく、これら 2 つの実装のパフォーマンスが おおむねどのように比較されるかの感覚をつかむことだからです。
より包括的なベンチマークを行うには、contents としてさまざまなサイズの
さまざまなテキストを使い、query として異なる単語や長さの異なる単語を
使い、そのほかあらゆる種類の変化を試すべきです。要点は次のとおりです。
イテレータは高水準の抽象化ではありますが、コンパイルされると、あたかも
自分でより低水準のコードを書いたかのような、ほぼ同じコードになります。
イテレータは Rust の ゼロコスト抽象化 の 1 つであり、これはその抽象化を
使っても実行時オーバーヘッドが一切追加されないことを意味します。これは、
C++ の元の設計者であり実装者である Bjarne Stroustrup が、2012 年の ETAPS
基調講演「Foundations of C++」でゼロオーバーヘッドを次のように定義して
いることに対応しています。
一般に、C++ の実装はゼロオーバーヘッドの原則に従います。使わないものに 対しては、コストを支払う必要はありません。さらに、使うものについても、 手書きでそれ以上に良くコード化することはできません。
多くの場合、イテレータを使った Rust コードは、自分で手書きするのと同じ アセンブリにコンパイルされます。ループ展開や配列アクセス時の境界チェックの 除去といった最適化が適用され、その結果のコードは非常に効率的になります。 これでこのことがわかったので、恐れることなくイテレータやクロージャを使う ことができます。これらを使うとコードはより高水準に見えますが、そのための 実行時パフォーマンスのペナルティは課されません。
まとめ
クロージャとイテレータは、関数型プログラミング言語の考え方に着想を得た Rust の機能です。これらは、高水準の考え方を低水準のパフォーマンスで明確に 表現する Rust の能力に貢献しています。クロージャとイテレータの実装は、 実行時パフォーマンスに影響を与えないようになっています。これは、ゼロコスト 抽象化を提供することを目指す Rust の目標の一部です。
I/O プロジェクトの表現力を改善したところで、次は cargo のさらにいくつかの
機能を見ていきましょう。これらは、このプロジェクトを世界と共有するのに
役立ちます。