foreach、パフォーマンス面で。変数をループの前または内部で一度宣言する必要がありますか?
-
06-07-2019 - |
質問
foreachステートメントの外側で変数を宣言し、毎回変数を再割り当てする(foreach)か、foreach内に新しい変数を作成することで、パフォーマンス面で優れているもの たとえば
private List<ListItem> GetItems()
{
var items = new List<ListItem>();
var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
ListItem item;
foreach (var i in collection)
{
item = new ListItem { Text = i.ToString() };
items.Add(item);
}
return items;
}
またはこれですか?
private List<ListItem> GetItems()
{
var items = new List<ListItem>();
var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (var i in collection)
{
ListItem item = new ListItem { Text = i.ToString() };
items.Add(item);
}
return items;
}
ここでアイテムオブジェクトについて話してください。 ありがとうございました。
解決
早期最適化のように聞こえます。
まず、ここにパフォーマンスの問題があると信じる理由はありますか?
次に、リリースビルドでは、コンパイラのオプティマイザーはおそらく両方のシナリオで同一のコードを生成します-おそらく無関係です。デバッグビルドでは、これは常に正しいとは限りませんが、デバッグビルドの目的はコードを正確にステップスルーできるようにすることであるため、最適化は望ましくありません。
他のヒント
これが重要なのは エッジケースです。 「キャプチャ」する場合匿名メソッド/ラムダへの変数。それ以外の場合は時期尚早で、違いはありません。まったく。
重要な場合の例:
// prints all items in no particular order
foreach (var i in collection)
{
string s = i.ToString();
ThreadPool.QueueUserWorkItem(delegate { Console.WriteLine(s); });
}
vs
// may print the same item each time, or any combination of items; very bad
string s;
foreach (var i in collection)
{
s = i.ToString();
ThreadPool.QueueUserWorkItem(delegate { Console.WriteLine(s); });
}
2つのコードブロックによって生成されたILが同一であると確信しています。パフォーマンスに変化はありません。ただし、使用する場所でアイテムのタイプを宣言する2番目のコードブロックは、もう少し読みやすく、使用します。
これは非常に微小な最適化であり、同一のコードを生成しない場合、両方の方法はおそらくパフォーマンスに関してまったく同じになります。この場合、読みやすくしてください。あなたのオブジェクトはforeachループ以外では何の役にも立たないので、私は2番目を好むでしょう。
おそらく、保存された参照をすべて削除することもできます:
private List<ListItem> GetItems()
{
var items = new List<ListItem>();
var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (var i in collection)
{
items.Add(new ListItem { Text = i.ToString() });
}
return items;
}
2つのブロックによって作成されるILはほぼ同じである必要があります。最適化を検討している場合は、最終的なリストの長さを設定してから項目を入力することを検討します。そうすれば、リストの長さを拡張しても拡張のペナルティはありません。
次のようなもの:
private List<ListItem> GetItems()
{
var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var items = new List<ListItem>(collection.Count); //declare the amount of space here
foreach (var i in collection)
{
ListItem item = new ListItem { Text = i.ToString() };
items.Add(item);
}
return items;
}
おそらく同じコードにコンパイルされますが、なぜそれを再宣言する必要があります。 これは、この例ではアイテムの参照についての素晴らしいことです。 完了したら、別のListItemとGCに割り当てることができます 残りの世話をします。
しかし、一方では他のプログラマーにとっての読みやすさ。これは、アプリケーションのパフォーマンスを劇的に変えることは決してない決定です。
あなたの場合はさらに良いです:
private List<ListItem> GetItems()
{
var items = new List<ListItem>();
var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (var i in collection)
items.Add(new ListItem { Text = i.ToString() });
return items;
}
なぜ余分な変数を作成するのですか?
誰もが推測したように、ILは同一です。また、他の人が言ったように、問題になるまでこのようなことを心配しないでください。代わりに、その変数のスコープがどこに属するかを自問してください。
そのコードブロックのスコープとコンテキストは、このシナリオでは本質的に時期尚早で不要な小さなパフォーマンス最適化よりもはるかに重要です。