C#/ VB.NET/.NETでは、for
またはforeach
のどちらのループが高速に実行されますか?
for
ループはforeach
ループよりも速く動作することを読んだので、 かなり前 すべてのコレクション、ジェネリックコレクション、すべての配列などに当てはまると仮定しました。
私はGoogleを精査し、いくつかの記事を見つけましたが、それらのほとんどは決定的ではなく(記事に関するコメントを読んで)、無制限です。
理想的なのは、各シナリオをリストし、同じシナリオに最適なソリューションを提供することです。
例(それがどうあるべきかの単なる例):
for
はforeach
よりも優れていますIList
(非ジェネリック)文字列を反復処理する場合-foreach
はfor
よりも優れています同じことについてウェブ上でいくつかの参照が見つかりました:
foreach
へ、またはforeach
へ、それは質問ですfor
vs foreach
[編集]
読みやすさの他に、私は事実と数字に本当に興味があります。パフォーマンスの最適化の最後のマイルが圧迫されるアプリケーションがあります。
Patrick Smacchia これについてのブログ記事 先月、以下の結論が得られました。
- リストのforループは、リストのforeachループよりも2倍以上安いです。
- 配列のループは、リストのループよりも約2倍安価です。
- 結果として、forを使用した配列のループは、foreachを使用したListのループよりも5倍安くなります(これは私たち全員が行うことです)。
foreach
ループは、for
ループよりも具体的な意図を示しています。
foreach
ループを使用すると、コレクション内の場所に関係なく、コレクションの各メンバーに対して何かを行うことを計画していることを、コードを使用するすべてのユーザーに示します。また、元のコレクションを変更していないことを示します(しようとすると例外がスローされます)。
foreach
のもう1つの利点は、IEnumerable
に対してのみ機能することです。ここで、for
は、各要素が実際にインデックスを持つIList
に対してのみ意味を持ちます。
ただし、要素のインデックスを使用する必要がある場合は、もちろんfor
ループの使用を許可する必要があります。ただし、インデックスを使用する必要がない場合、インデックスを作成するとコードが煩雑になります。
私が知っている限り、パフォーマンスに大きな影響はありません。将来のある段階では、foreach
を使用してコードを適応させて複数のコアで実行する方が簡単かもしれませんが、現時点では心配する必要はありません。
まず、 Dmitry's answer への反論。配列の場合、C#コンパイラは、同等のforeach
ループの場合とほぼ同じコードをfor
に対して発行します。これが、このベンチマークの結果が基本的に同じである理由を説明しています。
using System;
using System.Diagnostics;
using System.Linq;
class Test
{
const int Size = 1000000;
const int Iterations = 10000;
static void Main()
{
double[] data = new double[Size];
Random rng = new Random();
for (int i=0; i < data.Length; i++)
{
data[i] = rng.NextDouble();
}
double correctSum = data.Sum();
Stopwatch sw = Stopwatch.StartNew();
for (int i=0; i < Iterations; i++)
{
double sum = 0;
for (int j=0; j < data.Length; j++)
{
sum += data[j];
}
if (Math.Abs(sum-correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i=0; i < Iterations; i++)
{
double sum = 0;
foreach (double d in data)
{
sum += d;
}
if (Math.Abs(sum-correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds);
}
}
結果:
For loop: 16638
Foreach loop: 16529
次に、コレクションタイプが重要であるというGregのポイントの検証-上記の配列をList<double>
に変更すると、根本的に異なる結果が得られます。一般に大幅に遅くなるだけでなく、foreachはインデックスによるアクセスよりも大幅に遅くなります。そうは言っても、私はまだほぼ常にforeachよりもforeachを好むので、コードが簡単になります。最適化はめったにありません。
パフォーマンスをめぐる議論があるときはいつでも、ケースをサポートするために定量的な結果を使用できるように、小さなテストを書くだけです。
StopWatchクラスを使用して、精度を高めるために数百万回繰り返します。 (これはforループなしでは難しいかもしれません):
using System.Diagnostics;
//...
Stopwatch sw = new Stopwatch()
sw.Start()
for(int i = 0; i < 1000000;i ++)
{
//do whatever it is you need to time
}
sw.Stop();
//print out sw.ElapsedMilliseconds
指はこの結果を交差させて、違いは無視できることを示しており、最も保守性の高いコードで結果を何でもすることができます
常に近くにあります。配列の場合、時々for
は少し高速ですが、foreach
はより表現力があり、LINQなどを提供します。一般に、foreach
を使用します。
また、foreach
はいくつかのシナリオで最適化される場合があります。たとえば、リンクリストはインデクサーにはひどいかもしれませんが、foreach
には速いかもしれません。実際、標準のLinkedList<T>
はこの理由でインデクサーさえ提供していません。
私の推測では、おそらく99%のケースでは重要ではないので、なぜ最も適切なのではなく(理解しやすい/保守しやすいように)高速を選択するのですか?
preferforeach
ループよりもfor
ループを使用することには、非常に良い理由があります。 foreach
ループを使用できる場合、上司は正しいはずです。
ただし、すべての反復が単にリストを1つずつ順番に処理するわけではありません。彼がforbidddingforである場合、はい、それは間違っています。
もし私があなただったら、私がすることはあなたの自然なforループをすべて再帰に変えるです。それは彼に教えてくれますし、あなたにとっても良いメンタルエクササイズです。
2つの間に大きなパフォーマンスの違いはほとんどありません。いつものように、「どちらが速いですか?」に直面したとき質問、あなたは常に「これを測定できる」と考えるべきです。
ループの本体で同じことを行う2つのループを作成し、両方を実行して時間を計り、速度の違いを確認します。ほぼ空の本体と、実際に行うものに似たループ本体の両方でこれを行います。また、使用しているコレクションの種類で試してください。コレクションの種類によってパフォーマンスの特性が異なる可能性があるためです。
TechEd 2005のJeffrey Richter:
「C#コンパイラは基本的にうそつきです。」 ..「それは多くのものにあります。」 ..「foreachループを実行するときと同じように...」..「...これは、作成するコードの1行ですが、それを実行するためにC#コンパイラーが出力するものは驚くべきものです。 try/finallyブロック、finallyブロック内で変数をIDisposableインターフェイスにキャストします。キャストが成功すると、Disposeメソッドを呼び出し、ループ内でループ内でCurrentプロパティとMoveNextメソッドを繰り返し呼び出します。オブジェクトはカバーの下に作成されています。多くの人がforeachを使用しています。コーディングが非常に簡単で、実行が非常に簡単だからです。 ".." foreachは、代わりにsquareを使用してコレクションを反復した場合、インデックスを作成するだけのブラケット表記法は、はるかに高速であり、ヒープ上にオブジェクトを作成しません...」
オンデマンドWebキャスト: http://msevents.Microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US
ばかげてる。 forループ、パフォーマンス面などを禁止する説得力のある理由はありません。
パフォーマンスベンチマークおよびその他の引数については、 Jon Skeetのブログ を参照してください。
オブジェクトのコレクションを操作する場合は、foreach
の方が適していますが、数値を増やす場合は、for
ループの方が適しています。
最後のケースでは、次のようなことができることに注意してください。
foreach (int i in Enumerable.Range(1, 10))...
しかし、確かにパフォーマンスは良くありません。実際、for
に比べてパフォーマンスは劣ります。
これはあなたを救うはずです:
public IEnumerator<int> For(int start, int end, int step) {
int n = start;
while (n <= end) {
yield n;
n += step;
}
}
つかいます:
foreach (int n in For(1, 200, 4)) {
Console.WriteLine(n);
}
より大きな勝利を得るには、3つのデリゲートをパラメーターとして使用できます。
これには、ほとんどの「速い」質問と同じ2つの答えがあります。
1)測定しない場合、あなたは知りません。
2)(理由...)状況によります。
これは、反復するIEnumerableのタイプに対して、「this [int index]」メソッドのコストと比較して、「MoveNext()」メソッドのコストに依存します。
「foreach」キーワードは一連の操作の省略形です。IEnumerableでGetEnumerator()を1回呼び出し、反復ごとにMoveNext()を1回呼び出し、何らかの型チェックを行います。パフォーマンスの測定に影響を与える可能性が最も高いのは、MoveNext()がO(N)回呼び出されるためです。安いかもしれませんが、そうではないかもしれません。
「for」キーワードはより予測しやすいように見えますが、ほとんどの「for」ループ内には「collection [index]」のようなものがあります。これは単純な配列のインデックス操作のように見えますが、実際にはメソッド呼び出しであり、そのコストは繰り返し処理するコレクションの性質に完全に依存します。おそらく安いですが、そうでないかもしれません。
コレクションの基本構造が本質的にリンクリストである場合、MoveNextは非常に安価ですが、インデクサーのコストはO(N)になり、「for」ループの真のコストはO(N * N)になります。 。
「forループを使用しても問題ないことを彼に納得させるために使用できる引数はありますか?」
いいえ、上司がどのプログラミング言語の構成を使用するかを指示するレベルまでマイクロ管理している場合は、何も言えません。ごめんなさい。
for
-とforeach
- loopの速度の違いは、配列やリストなどの一般的な構造をループしているときは小さく、コレクションに対してLINQ
クエリを実行することは、ほとんどの場合わずかに遅くなります。他のポスターが言ったように、1ミリ秒の余分なパフォーマンスではなく、表現力を求めます。
これまでに言われていないことは、foreach
ループがコンパイルされると、繰り返し処理されるコレクションに基づいてコンパイラーによって最適化されるということです。つまり、どのループを使用するかわからない場合は、foreach
ループを使用する必要があります。コンパイルすると、最適なループが生成されます。読みやすくなっています。
foreach
ループのもう1つの重要な利点は、コレクションの実装が(たとえば、int array
からList<int>
に)変更された場合、foreach
ループでコードの変更が不要になることです。
foreach (int i in myCollection)
上記は、コレクションのタイプに関係なく同じですが、for
ループでは、myCollection
をarray
からList
に変更した場合、以下はビルドされません。
for (int i = 0; i < myCollection.Length, i++)
おそらく、列挙しているコレクションのタイプとそのインデクサーの実装に依存します。ただし一般的には、foreach
を使用する方が適切な方法です。
また、インデクサーだけでなく、どのIEnumerable
でも機能します。
すべての言語構成体には、使用に適した時間と場所があります。 C#言語には4つの別個の 反復ステートメント があり、それぞれが特定の目的のために存在し、適切に使用される理由があります。
上司と一緒に座って、for
ループに目的がある理由を合理的に説明することをお勧めします。 for
反復ブロックは、foreach
反復よりもアルゴリズムをより明確に記述する場合があります。これが当てはまる場合は、それらを使用するのが適切です。
また、上司にも指摘します。パフォーマンスは問題ではなく、実用的な問題でもありません。それは、アルゴリズムを簡潔で意味のある、保守可能な方法で表現することの問題です。このようなマイクロ最適化では、パフォーマンスの最適化のポイントが完全に失われます。実際のパフォーマンス上の利点は、ループの再構築ではなく、アルゴリズムの再設計とリファクタリングから得られるためです。
合理的な議論の後、この権威主義的見解がまだある場合、どのように進めるかはあなた次第です。個人的には、合理的な思考が妨げられている環境で働くのは幸せではなく、別の雇用主の下で別の職に移ることを検討します。ただし、動揺する前に話し合うことを強くお勧めします。単純な誤解があるかもしれません。
for
がforeach
より高速であるかどうかは、本当に重要です。どちらかを選択すると、パフォーマンスに大きな影響を与えることを真剣に疑っています。
アプリケーションを最適化する最良の方法は、実際のコードのプロファイリングです。それは、最も作業/時間を占めるメソッドを特定します。最初にそれらを最適化します。それでもパフォーマンスが許容できない場合は、手順を繰り返します。
一般的な規則として、マイクロ最適化は重要な利益をもたらすことはめったにないので、マイクロ最適化から離れることをお勧めします。唯一の例外は、特定されたホットパスを最適化する場合です(つまり、プロファイリングで使用頻度の高いメソッドがいくつか特定された場合、これらを広範囲に最適化するのが理にかなっています)。
それはあなたがすることですinside実際のループ構造ではなく、パフォーマンスに影響するループです(あなたのケースが自明でないと仮定して)。
2つはほぼ同じ方法で実行されます。両方を使用するコードをいくつか書いてから、ILを見せてください。同等の計算が表示されるはずです。つまり、パフォーマンスに違いはありません。
Deep .NET-part 1 Iteration でそれについて読むことができます
.NETソースコードから逆アセンブリまでの結果(最初の初期化なし)をカバーします。
本当に頭をねじ込んで、代わりにIQueryable .foreachクロージャーを探してください:
myList.ForEach(c => Console.WriteLine(c.ToString());
LOL
forは、実装するより単純なロジックを持っているため、foreachよりも高速です。
ほとんどの場合、実際には違いはありません。
通常、明示的な数値インデックスがない場合は常にforeachを使用する必要があり、実際に反復可能なコレクションがない場合は常に使用する必要があります(たとえば、上の三角形の2次元配列グリッドで反復する) 。選択肢がいくつかある場合があります。
Forループは、マジックナンバーがコードに現れ始めた場合、維持が少し難しくなる可能性があると主張することができます。 forループが禁止されているという理由だけで、forループを使用できず、コレクションをビルドするか、ラムダを使用してサブコレクションをビルドする必要があることに悩まされるのは当然です。
Jeffrey Richterは、最近のポッドキャストでforとforeachのパフォーマンスの違いについて話しました: http://pixel8.infragistics.com/shows/everything.aspx#Episode:9317
foreach
を繰り返し処理するList
ループを見つけましたfaster。以下のテスト結果をご覧ください。以下のコードでは、時間を測定するためにarray
およびfor
ループを使用して、サイズ100、10000および100000のforeach
を個別に繰り返します。
private static void MeasureTime()
{
var array = new int[10000];
var list = array.ToList();
Console.WriteLine("Array size: {0}", array.Length);
Console.WriteLine("Array For loop ......");
var stopWatch = Stopwatch.StartNew();
for (int i = 0; i < array.Length; i++)
{
Thread.Sleep(1);
}
stopWatch.Stop();
Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds);
Console.WriteLine(" ");
Console.WriteLine("Array Foreach loop ......");
var stopWatch1 = Stopwatch.StartNew();
foreach (var item in array)
{
Thread.Sleep(1);
}
stopWatch1.Stop();
Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds);
Console.WriteLine(" ");
Console.WriteLine("List For loop ......");
var stopWatch2 = Stopwatch.StartNew();
for (int i = 0; i < list.Count; i++)
{
Thread.Sleep(1);
}
stopWatch2.Stop();
Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds);
Console.WriteLine(" ");
Console.WriteLine("List Foreach loop ......");
var stopWatch3 = Stopwatch.StartNew();
foreach (var item in list)
{
Thread.Sleep(1);
}
stopWatch3.Stop();
Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds);
}
@jgauffinの提案の後、@ johnskeetコードを使用し、for
を使用したarray
ループは、以下よりも高速であることがわかりました。
以下のテスト結果とコードを参照してください。
private static void MeasureNewTime()
{
var data = new double[Size];
var rng = new Random();
for (int i = 0; i < data.Length; i++)
{
data[i] = rng.NextDouble();
}
Console.WriteLine("Lenght of array: {0}", data.Length);
Console.WriteLine("No. of iteration: {0}", Iterations);
Console.WriteLine(" ");
double correctSum = data.Sum();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
{
double sum = 0;
for (int j = 0; j < data.Length; j++)
{
sum += data[j];
}
if (Math.Abs(sum - correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("For loop with Array: {0}", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (var i = 0; i < Iterations; i++)
{
double sum = 0;
foreach (double d in data)
{
sum += d;
}
if (Math.Abs(sum - correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("Foreach loop with Array: {0}", sw.ElapsedMilliseconds);
Console.WriteLine(" ");
var dataList = data.ToList();
sw = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
{
double sum = 0;
for (int j = 0; j < dataList.Count; j++)
{
sum += data[j];
}
if (Math.Abs(sum - correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
{
double sum = 0;
foreach (double d in dataList)
{
sum += d;
}
if (Math.Abs(sum - correctSum) > 0.1)
{
Console.WriteLine("Summation failed");
return;
}
}
sw.Stop();
Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds);
}
特定の速度最適化プロセスを行っているのでなければ、コードの読み取りと保守が最も簡単な方法を使用してください。
いずれかのコレクションクラスのようにイテレータが既にセットアップされている場合、foreachは簡単なオプションです。そして、それがあなたが反復している整数範囲であるなら、forはおそらくもっときれいです。
Forループのようなものの使用を完全に禁止するのは少し奇妙に思えます。
興味深い記事があります here 2つのループ間のパフォーマンスの違いの多くをカバーしています。
個人的にはforeachループの方がforeachの方が少し読みやすいと思いますが、手元の仕事に最適なものを使用し、forループの方が適切であれば、foreachループを含めるために余分な長いコードを書く必要はありません。
両者の間に「巨大な」パフォーマンスの違いが見つかるとは誰も期待しないでしょう。
答えは、アクセスしようとしているコレクションのインデクサーアクセスの実装が速いか、IEnumeratorアクセスの実装が速いかによって異なると思います。 IEnumeratorは多くの場合、インデクサーを使用し、現在のインデックス位置のコピーを保持するだけなので、列挙子へのアクセスは少なくとも直接的なインデックスアクセスと同じかそれより遅くなると予想されますが、それほど大きくはなりません。
もちろん、この答えはコンパイラが実装する最適化を考慮していません。
Forループとforeachループは必ずしも同等ではないことに注意してください。リストが変更された場合、リスト列挙子は例外をスローしますが、通常のforループでは常にその警告が表示されるとは限りません。リストが間違った時間に変更された場合、別の例外が発生することさえあります。
しばらく前にテストしましたが、その結果、for
ループはforeach
ループよりもはるかに高速です。原因は簡単です。foreach
ループは、最初にコレクションのIEnumerator
をインスタンス化する必要があります。
Windows Mobileのプロジェクトでは、コントロールコレクションにfor
ループを使用しました。 20個のコントロールで100ミリ秒かかりました! foreach
ループは4µmsのみを使用しました。それはパフォーマンスの問題でした...
私はforeachの方法がForよりもWAY速い場合に遭遇しました
なぜforeachがrichtextbox行を読み込む際のforループよりも速いのか
私はその質問でOPに似たケースがありました。
約72K行を読み取るテキストボックスで、Linesプロパティ(実際にはgetterメソッド)にアクセスしていました。 (そして明らかにwinformにはO(1)ではないゲッターメソッドがあります。O(n)だと思うので、テキストボックスが大きいほど、その「プロパティ」から値を取得するのに時間がかかります。私が持っていたループにはfor(int i=0;i<textBox1.lines.length;i++) str=textBox1.Lines[i]
があり、行を読むたびにテキストボックス全体を読んでいて、条件をチェックするたびにテキストボックス全体を読んでいたので、本当にかなり遅かったです。
Jon Skeetは、Linesプロパティに1回だけアクセスできることを示しています(反復ごとに1回でも、1回でも)。各反復で2回ではなく(これは膨大な時間です)。 string [] strarrlines = textBox1.Lines;を実行します。そしてストララインをループします。
しかし、確かにかなり直感的なフォームでforループを使用し、Linesプロパティにアクセスすると、非常に非効率的です。
for (int i = 0; i < richTextBox.Lines.Length; i++)
{
s = richTextBox.Lines[i];
}
テキストボックスまたはリッチテキストボックスの場合、非常に遅いです。
リッチテキストボックスでそのループをテストするOPは、「15000 lines.forループでは15000行にループするのに8分かかりましたが、foreachはそれを列挙するのにほんの少しかかりました。」
そのリンクのOPは、このforeachが、上記のforループ(同じOP)よりもはるかに効率的であることがわかりました。私がやったように。
String s=String.Empty;
foreach(string str in txtText.Lines)
{
s=str;
}
Forおよびforeachでの収集速度に基づいてこの詳細を述べました。
リスト-ForループはForeachループよりもわずかに高速です
ArrayList-ForループはForeachループよりも約2倍以上高速です。
配列-両方とも同じ速度ですが、Foreachループは少し高速のようです。
「Writing High-Performance .NET Code」の著者であるベン・ワトソン:
「これらの最適化はプログラムにとって重要ですか?プログラムがCPUバウンドであり、コレクションの反復が処理のコア部分である場合のみ。あなたが見るように、注意しないとパフォーマンスを損なう方法がありますが、私の哲学はこれです。ほとんどの人はこれを知る必要はありませんが、知っている場合は、システムのすべての層を理解することが重要であり、インテリジェントな選択を行うことができます。 。
最も重要な説明はこちらにあります: http://www.codeproject.com/Articles/844781/Digging-Into-NET-Loop-Performance-Bounds-checking
少なくとも私の同僚やそれ以上のことを言っているのを見たことはありません。それはfor
name__とforeach
name__の間に著しい速度差がないという事実を考えるとばかげています。彼がすべての場合にそれを使用することを求めている場合、同じことが当てはまります!
.NET Framework 4以降では、次のようにParallel.ForとParallel.ForEachを使用することもできます。 C#マルチスレッドループとParallel.ForまたはParallel.ForEach
Forはほとんどの場合foreachよりもわずかに速いと思いますが、これは本当にポイントがありません。私が言及していないことの1つは、あなたが話しているシナリオ(つまり、大容量のWebアプリ)では、forとforeachのパフォーマンスの違いはサイトのパフォーマンスに影響を与えないということです。 v。foreachではなく、要求/応答時間とDB時間によって制限されます。
とはいえ、あなたはforeachに対する嫌悪感を理解していません。私の意見では、foreachは通常、どちらかを使用できる状況でより明確です。私は通常、collectionい、非標準的な方法でコレクションを横断する必要がある状況のために予約しています。
3つのネストされたループ(List<MyCustomType>
)を使用して、いくつかの大きなデータの解析を行う必要がありました。上記のRob Fonseca-Ensorの投稿を使用して、forとforeachを使用して時間を比較し、違いを比較することは興味深いと思いました。
違いは、foreach(foreach {foreach {forech {}}}のようにネストされた3つのforeachが171.441秒でジョブを実行しましたが、for(for {for {for {}}})は158.616秒。
現在、13秒は約13%これを行う時間の短縮です。これは私にとってはかなり重要です。ただし、foreachは、3つのindexed-forsを使用するよりも間違いなく読みやすいです...
具体的な答えについては、 this を読むことをお勧めします。この記事の結論は、forループの使用は一般にforeachループよりも優れており、高速であるということです。