質問
GetEnumerator()を使用してIEnumerator.Currentをキャストするのは高価だと思います。より良い提案はありますか?
似たような機能でパフォーマンスが向上する場合は、別のデータ構造を使用できます。
考えた後:
キャストが不要になるように、汎用スタックの方が良いでしょうか?
解決
ベンチマークを行ったことがありますか、それとも単なる感情に過ぎませんか?
処理時間の大半がスタックのループ処理に費やされていると思われる場合は、ベンチマークを実行し、そうであることを確認する必要があります。その場合、いくつかのオプションがあります。
- ループが不要になるようにコードを再設計します
- より高速なループ構造を見つけます。 (それほど重要ではありませんが、ジェネリックをお勧めします。再び、ベンチマークを行います。)
編集:
不要なループの例は、リスト内で検索を実行したり、2つのリストなどに一致させたりする場合です。ループに時間がかかる場合は、リストをバイナリツリーまたはハッシュマップに入れるのが適切かどうかを確認してください。それらを作成するには初期費用が発生する可能性がありますが、コードが再設計された場合、後でO(1)ルックアップを行うことでそれを取り戻すことができます。
他のヒント
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.
}
ジェネリック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には意図しない割り当てスパイクがあり、最終的に<!> quot; slow down <!> quot;アプリケーションのパフォーマンス。
列挙子を作成する代わりに、ToArrayメソッドを使用して、配列を反復処理することもできます。スタック反復子は、スタックが変更されたかどうかをチェックするための若干のオーバーヘッドを引き起こしますが、配列の反復は高速です。ただし、そもそも配列を作成するオーバーヘッドがあります。 matsが言うように、選択肢をベンチマークする必要があります。