GetEnumerator() を使用して IEnumerator.Current をキャストするのはコストがかかると感じています。より良い提案はありますか?
より優れたパフォーマンスで同様の機能を提供する場合は、別のデータ構造を使用することにオープンです。
考えた後:
キャストが不要になるように、一般的なスタックの方が良いアイデアでしょうか?
GetEnumerator() を使用して IEnumerator.Current をキャストするのはコストがかかると感じています。より良い提案はありますか?
より優れたパフォーマンスで同様の機能を提供する場合は、別のデータ構造を使用することにオープンです。
考えた後:
キャストが不要になるように、一般的なスタックの方が良いアイデアでしょうか?
Stack<T>
(foreach を使用すると) 確かにキャストを節約できますが、実際にはボクシングは物事の壮大な計画においてそれほど悪くはありません。パフォーマンスに問題がある場合、これが大きな価値を追加できる領域であるとは思えません。プロファイラーを使用し、実際の問題に焦点を当てます。そうでなければ、これは時期尚早です。
データを 1 回だけ読み取りたい場合 (つまり、スタックを消費しても構わない場合)、これはより高速になる可能性があることに注意してください (列挙子のオーバーヘッドを回避します)。YMMV。
Stack<T> stack = null;
while (stack.Count > 0)
{
T value = stack.Pop();
// process value
}
スタックの機能が必要な場合 (リストやその他のコレクション型に追加されたものとして)、はい、汎用スタックを使用してください。これにより、コンパイラが実行時にキャストをスキップするため、処理が少し高速化されます (コンパイル時に保証されるため)。
Stack<MyClass> stacky = new Stack<MyClass>();
foreach (MyClass item in stacky)
{
// this is as fast as you're going to get.
}
ベンチマークを行ったことはありますか、それとも直感的なものですか?
処理時間の大部分がスタックのループに費やされていると思われる場合は、ベンチマークを行い、それが事実であることを確認する必要があります。その場合、いくつかのオプションがあります。
編集:
必要のないループの例としては、リスト内でルックアップを実行したり、2 つのリストを照合したりする場合などがあります。ループに時間がかかる場合は、リストをバイナリ ツリーまたはハッシュ マップに入れることが理にかなっているのかどうかを確認してください。それらを作成するには初期コストがかかる可能性がありますが、コードが再設計された場合、後で O(1) ルックアップを使用することでコストを取り戻すことができます。
はい、一般的なスタックを使用すると、キャストが節約されます。
ジェネリックを列挙するIEnumerable<T>
かIEnumerator<T>
、反復変数が T 型の場合はキャストを作成しないため、ほとんどの場合、ジェネリックを使用すると高速になりますが、特に値型で使用する場合、ジェネリックには非常に微妙な問題がいくつかあります。
Rico Mariani (Microsoft パフォーマンス アーキテクト) が、相違点と基盤について詳しく説明している投稿をいくつか持っています。
速度に関する限り、コンテキストに応じて複数の変数があります。たとえば、C# のような自動メモリ管理のコードベースでは、ゲームなどでフレームレートに影響を与える割り当てスパイクが発生する可能性があります。foreach の代わりにこれを行うことができる優れた最適化は、while ループを持つ列挙子です。
var enumerator = stack.GetEnumerator();
while(enumerator.MoveNext ()) {
// do stuff with enumerator value using enumerator.Current
enumerator.Current = blah
}
CPU ベンチマークに関する限り、これはおそらく foreach よりも高速ではありませんが、foreach には意図しない割り当てスパイクが発生する可能性があり、最終的にアプリケーションのパフォーマンスが「低下」する可能性があります。
列挙子を作成する代わりに、ToArray メソッドを使用して、配列を反復処理することもできます。スタック反復子は、スタックが変更されたかどうかを確認するためにわずかなオーバーヘッドを引き起こしますが、配列に対する反復処理は高速です。ただし、最初に配列を作成するオーバーヘッドはもちろんあります。マットが言うように、代替案をベンチマークする必要があります。