.NETでは、「for」または「foreach」のどちらのループが速く実行されますか?


345

C#/ VB.NET / .NETでは、どのループがより速く実行されますforforeach

ループがずっと前にループforよりも速く機能することを読んだので、それがすべてのコレクション、ジェネリックコレクション、すべての配列などに当てはまると思いました。foreach

私はGoogleを調べていくつかの記事を見つけましたが、それらのほとんどは決定的ではなく(記事に関するコメントを読んで)、自由回答です。

理想的なのは、各シナリオをリストして、それらの最良のソリューションを用意することです。

たとえば(それがどうあるべきかのほんの一例):

  1. 1000以上の文字列の配列を反復するため- forより良いですforeach
  2. IList(非ジェネリック)文字列を反復するため- foreachより優れていますfor

同じことをウェブ上で見つけたいくつかの参考文献:

  1. エマニュエルシャンツァーによるオリジナルの壮大な古い記事
  2. CodeProject FOREACH対。ために
  3. ブログ-するべきforeachかどうかforeach、それが問題です
  4. ASP.NETフォーラム-NET 1.1 C#forvsforeach

[編集]

読みやすさの側面は別として、私は事実と数字に本当に興味があります。絞り込まれたパフォーマンス最適化の最後の1マイルが問題になるアプリケーションがあります。


3
違いはまだ存在しています。特に配列はforeachと同じくらい高速でなければなりませんが、他のすべての場合、プレーンループの方が高速です。もちろん、ほとんどの場合、これで違いが生じることはありません。もちろん、賢いJITコンパイラーは理論的にはその違いを取り除くことができます。
2008

3
コンテキストがないと、あなたが何をしているのか正確にはわかりませんが、部分的に満たされた配列に遭遇するとどうなりますか?
Chris Cudmore、

6
ちなみに、1か月あたり200万ヒットは恐ろしいものではありません。これは、平均で1秒あたりのヒット数を下回ります。
Mehrdad Afshari、

37
重要な注意:この質問は昨日、C#のforeach代わりに使用することを余儀なくされたというまったく関係のない質問とマージされましたfor。ここでまったく意味のない答えを見つけた場合、それが理由です。不運な答えではなく、モデレーターを責めます。
TED

7
@TEDああ、私はすべての「あなたの上司は馬鹿です」コメントがどこから来たのかと思っていました、ありがとう
Gaspa79

回答:


350

Patrick Smacchiaは先月、このことについてブログを書き、次のような結論を出しました。

  • リストのforループは、リストのforeachループよりも2倍以上安価です。
  • 配列のループは、リストのループより約2倍安価です。
  • 結果として、foreachを使用した配列でのループは、foreachを使用したListでのループよりも5倍安価です(これは私たち全員が行うことです)。

130
ただし、忘れないでください。「時期尚早の最適化はすべての悪の根源です。」
Oorang

18
@Hardwareguy:forがほとんど気付かれないほど高速であることがわかったら、なぜそれを一般的に使い始めるべきではないのですか?余分な時間はかかりません。
DevinB 2009

47
@devinb、「for」を使用することは、コード、別の変数、チェックする必要がある条件などを追加するため、「foreach」を使用するよりも困難です。「foreach」ループで1つずれるエラーが何回発生したか?
2009

35
@Hardwareguy、これが正解かどうか見てみましょう。でforeach配列をループする場合よりもリストをループする場合の方が5倍長くかかりforます。この種のパフォーマンスの違いは、アプリケーションにとって重要である場合もあれば、そうでない場合もありますが、手に負えないだけではありません。
ロバートハーベイ、

44
ブログ投稿を読むと、テストがリリースではなくデバッグで実行されているように見えるため、要因がある可能性があります。さらに、違いは特にループのオーバーヘッドのみです。ループの本体を実行する時間にはまったく影響しません。ほとんどの場合、リストの次の要素に移動するのにかかる時間よりもはるかに長くなります。問題があることを明確に特定し、アプリの違いを具体的に測定し、顕著な改善はありますが、すべてforeachのを削除するための一般的なアドバイスではないことを確認すると、良い情報になります。
Davy8、

164

まず、Dmitryの(現在は削除されている)回答に対する反訴。配列の場合、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>上記の配列をa に変更すると、根本的に異なる結果が得られます。一般に、速度が大幅に遅くなるだけでなく、foreachはインデックスによるアクセスよりも大幅に遅くなります。とは言っても、コードを単純化するforループよりもほとんど常に foreachを優先します。なぜなら、可読性はほとんど常に重要ですが、マイクロ最適化はめったにありません。


「上記の配列をList <double>に変更すると、根本的に異なる結果が得られます」興味深いことに、私はそれについて考えていませんでした
johnc

5
私のテストと他の人々のベンチマークとの間の結果の奇妙な違いを考えると、これはブログ投稿に値すると思います...
Jon Skeet

1
ほとんどの場合、アレイ間でどちらを好みますList<T>か?その場合も可読性はマイクロ最適化よりも優先されますか?
JohnB

12
@JohnB:うん-私はほとんど常にList<T>配列よりも好みます。例外はchar[]byte[]通常のコレクションではなく、データの「チャンク」として扱われることが多いです。
Jon Skeet、

驚くべきことに、私は自分のマシンでさらに積極的な違いを得ています。単純な配列でのforeachの方が10%近く賛成です。これはすべて、余分な変数やバウンドチェックなどを心配する必要がないジッターに起因するものだと思います。誰かがこれについて深い説明があれば、非常に興味深いでしょう。
Tamir Daniely

162

foreachループは、forループよりも具体的な意図を示しています

foreachループを使用すると、コレクション内の場所に関係なく、コレクションの各メンバーに対して何かを行うことを計画していることをコードを使用するすべての人に示します。また、元のコレクションを変更していないことも示します(変更しようとすると例外がスローされます)。

他の利点はforeach、それがどの上で動作することでIEnumerable、どことしてforのみ意味をなすIList各要素は、実際にインデックスを有する場合、。

ただし、要素のインデックスを使用する必要がある場合は、もちろんforループの使用を許可する必要があります。しかし、インデックスを使用する必要がない場合は、インデックスを作成するだけでコードが乱雑になります。

私の知る限り、パフォーマンスに大きな影響はありません。将来のある段階では、foreach複数のコアで実行するためにを使用してコードを適応させる方が簡単かもしれませんが、それは現在心配することではありません。


23
ctford:いいえ、違います。コンパイラは確かにの要素を並べ替えることはできませんforeachforeach関数型プログラミングとはまったく関係ありません。それは完全にプログラミングの必須パラダイムです。TPLとPLINQで起こっていることをに誤ってアトリビューションしていforeachます。
Mehrdad Afshari、

13
@BlueTrin:それは確かに順序を保証します(C#仕様セクション8.8.4 foreachwhileループの同等物として正式に定義しています)。@ctfordが参照していることは知っていると思います。タスク並列ライブラリを使用すると、基になるコレクションが要素を任意の順序で(.AsParallel列挙型を呼び出すことで)提供できます。foreachここでは何もせず、ループの本体は単一のスレッドで実行されます。並列化されるのは、シーケンスの生成だけです。
Mehrdad Afshari、

6
Enumerable.Selectには、アイテムのインデックスを取得できるオーバーロードがあるため、インデックスの必要性でもforを使用する必要はありません。msdn.microsoft.com/en-us/library/bb534869.aspx
TrueWill

5
ForEachは読みやすさとタイピングの節約に便利です。ただし、コストは重要です。(リスト内の各objについて)から(i = 0からlist.count-1まで)に作成したドキュメントデザイナーUIで2つまたは3つのforループを変更すると、編集ごとの応答時間が2〜3秒から約に短縮されました。数百のオブジェクトをループする小さなドキュメントの編集ごとに5秒。巨大なドキュメントであっても、すべてをループする時間は増えません。これがどうして起こったのか、私にはわかりません。私が知っているのは、代替案がオブジェクトのサブセットのみをループする複雑なスキームだったことです。いつでも5分お休みします!- マイクロ最適化ではありません
FastAl 2011年

5
@FastAl 通常のリストのパフォーマンスforeachとの違いは、for数百万を超えるアイテムを反復する場合の1秒の数分の1なので、少なくとも数百のオブジェクトでは、問題がforeachのパフォーマンスに直接関係していないことは確かです。あなたが使っていたどんなリストでも壊れた列挙子実装のように聞こえます。
Mike Marynowski 2017年

53

パフォーマンスに関する議論がある場合は常に、定量的な結果を使用してケースをサポートできるように、小さなテストを作成する必要があります。

正確を期すために、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

指がこの結果を横切ったことは、違いがごくわずかであることを示しており、最も保守しやすいコードで結果を何でも行うこともできます


13
しかし、forとforeachのパフォーマンスを比較することはできません。それらは異なる状況で使用されることになっています。
Michael Krelin-ハッカー、2009年

7
マイケルに同意します。パフォーマンスに基づいてどちらを使用するかは選択しないでください。最も意味のあるものを選択する必要があります。しかし、上司が「foreachより遅いのでforを使用しないでください」と言った場合、これは違いが無視できることを彼に納得させる唯一の方法です
Rob Fonseca-Ensor

「(これはforループなしでは難しいかもしれません)」または、whileループを使用できます。
jonescb 2009

49

いつも近くにあります。配列の場合 for、わずかに高速になることforeachありますが、表現力が高く、LINQなどを提供します。一般的には、foreach

さらに、foreachいくつかのシナリオで最適化される場合があります。たとえば、リンクされたリストはインデクサーにとってはひどいかもしれませんが、によっては速いかもしれませんforeach。実際、この規格でLinkedList<T>は、この理由からインデクサーすら提供されていません。


それであなたLinkedList<T>はよりも無駄がないと言っていますList<T>か?foreach(の代わりにfor)常に使用する場合は、使用した方がよいLinkedList<T>でしょう。
JohnB

2
@JohnB-より無駄のない; ただ違う。たとえば、リンクリストの各ノードには、フラットアレイ(これもを支えるList<T>)には不要な追加の参照があります。挿入 / 取り外しの方が安くなります。
マークグラベル

36

私の推測では、99%のケースではおそらくそれは重要ではないと思いますが、なぜ最も適切な(理解/保守が最も簡単な)代わりに、より高速を選択するのでしょうか?


6
@klew、実際にコードをプロファイリングする場合、どの20%が可能な限り高速である必要があるかを推測する必要はありません。また、高速にする必要のある実際のループ数がはるかに少ないこともわかるでしょう。さらに、あなたは本当にループの行為があなたがそのループで実際に行うこととは対照的にあなたが時間を費やす場所であると言っていますか?
2009

32

2つの間に大きなパフォーマンスの違いがある可能性はほとんどありません。いつものように、「どちらが速い?」に直面したとき。質問、あなたはいつも「私はこれを測定できる」と考えるべきです。

ループの本体で同じことを行う2つのループを記述し、両方を実行して時間を計測し、速度の違いを確認します。ほぼ空のボディと、実際に行うのと同様のループボディの両方でこれを行います。また、コレクションのタイプによってパフォーマンス特性が異なる可能性があるため、使用しているコレクションタイプで試してみてください。


32

ループより foreachループを優先する非常に良い理由がありforます。foreachループを使用できる場合、上司は当然のことです。

ただし、すべての反復がリストを1つずつ順番に処理するだけではありません。もし彼が禁じられているなら、はいそれは間違っています。

私があなただったら、私がやろうとしていることは、あなたの自然なforループをすべて再帰に変えることです。それは彼に教えるでしょう、そしてそれはあなたにとって良い精神的な練習でもあります。


再帰は、forループやforeachループのパフォーマンスと比較してどうですか?
JohnB 2010年

場合によります。末尾再帰を使用していて、コンパイラが十分にスマートである場合は、同じものにすることができます。OTOH:そうではなく、多くの不必要な(不変の)データをパラメーターとして渡す、またはスタック上で大きな構造をローカルとして宣言するなどの愚かなことをすると、本当に遅くなる(またはRAMが不足する)場合があります。
TED

ああ。なぜ今あなたがそれを尋ねたのか分かります。この答えはまったく別の質問に行きました。奇妙な理由で、Jonathan Sampsonは昨日2つを合併しました。彼は本当にそうすべきではなかった。マージされた回答は、ここではまったく意味がありません。
TED

18

TechEd 2005のJeffrey Richter:

「C#コンパイラーは基本的に私にはうそつきです。..「それは多くのことについてあります。」.. "foreachループを実行するときと同じように..." .. "...これは、作成するコードの1行ですが、C#コンパイラーが驚くべきことを行うために出力するものです。そこにtry / finallyブロックし、finallyブロック内で変数をIDisposableインターフェイスにキャストします。キャストが成功すると、Disposeメソッドが呼び出され、ループ内でCurrentプロパティとMoveNextメソッドがループ内で繰り返し呼び出されます。オブジェクトはカバーの下に作成されています。多くの人々は、foreachを使用しています。コーディングが非常に簡単で、実行が非常に簡単だからです。

オンデマンドWebキャスト:http : //msevents.microsoft.com/CUI/WebCastEventDetails.aspx? EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US


12

ばかげてる。forループ、パフォーマンスなどを禁止する理由はありません。

パフォーマンスベンチマークとその他の引数については、Jon Skeetのブログを参照してください。


2
更新されたリンク:codeblog.jonskeet.uk/2009/01/29/...
マット

より高速なループ構造は、何を反復する必要があるかによって異なります。DataRowsやカスタムオブジェクトなど、複数の種類のオブジェクトに対する複数の反復をベンチマークする別のブログ。また、forおよびforeachループ構造だけでなく、Whileループ構造のパフォーマンスも含まれます。
Free Coder 24 '20

11

オブジェクトのコレクションで作業する場合foreachはより良いですが、数値をインクリメントする場合はforループの方が適しています。

最後のケースでは、次のようなことができることに注意してください:

foreach (int i in Enumerable.Range(1, 10))...

しかし、確かにパフォーマンスは良くありませんfor。実際には、と比べてパフォーマンスが劣ります。


"より良い"は議論の余地があります:それは遅く、dnspyデバッガーはC#foreachに侵入しません(ただし、VS2017デバッガーはそうします)。読みやすくなることもありますが、それなしで言語をサポートすると、煩わしい場合があります。
Zeek2

10

これはあなたを救うはずです:

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つのデリゲートをパラメーターとして取ることができます。


1
小さな違いの1つforは、通常、範囲の終わりを除外するようにループが作成されることです(例:)0 <= i < 10Parallel.Forまた、一般的なforループと簡単に交換できるようにします。
Groo、2014

9

あなたはそれについてDeep .NETで読むことができます-パート1反復

.NETソースコードから逆アセンブリまでの結果(最初の初期化なし)をカバーしています。

例-foreachループを使用した配列反復: ここに画像の説明を入力してください

および-foreachループを使用した反復のリスト: ここに画像の説明を入力してください

そして最終結果: ここに画像の説明を入力してください

ここに画像の説明を入力してください


8

for-とforeach-ループの速度の違いは、配列、リストなどの一般的な構造をループしているときにLINQわずかであり、コレクションに対してクエリを実行すると、ほとんどの場合少し遅くなりますが、書く方が優れています!他のポスターが言ったように、ミリ秒の余分なパフォーマンスではなく表現力を追求してください。

これまで述べられていないforeachことは、ループがコンパイルされるとき、ループは、反復しているコレクションに基づいてコンパイラーによって最適化されるということです。つまり、どのループを使用するかわからない場合は、ループを使用する必要がありforeachます。コンパイルすると、最適なループが生成されます。それも読みやすいです。

もう一つの重要な利点foreachループがあること(int型からあなたのコレクションの実装が変更された場合arrayList<int>例えば)は、あなたのforeachループは任意のコードの変更を必要としません。

foreach (int i in myCollection)

上記のどのようなあなたのコレクションを入力すると、あなたの中のに対し、ある関係なく同じではありませんforあなたが変更された場合は、ループ、以下はビルドしないだろうmyCollectionからarrayList

for (int i = 0; i < myCollection.Length, i++)

7

「forループが受け入れられると彼を納得させるために使用できる引数はありますか?」

いいえ、上司が使用するプログラミング言語コンストラクトを指示するレベルまで細かく管理している場合、実際には何も言えません。ごめんなさい。


7

おそらく、列挙するコレクションのタイプとインデクサーの実装によって異なります。ただし、一般的には、使用foreachする方が適切な方法です。

また、IEnumerableインデクサーの機能だけでなく、どの機能でも機能します。


7

これには、ほとんどの「どちらが速いか」という質問と同じ2つの答えがあります。

1)測定しないとわかりません。

2)(なぜなら...)状況によります。

これは、反復するIEnumerableのタイプ(1つまたは複数)に対する「this [int index]」メソッドのコストと比較して、「MoveNext()」メソッドのコストに依存します。

「foreach」キーワードは、一連の操作の省略形です。IEnumerableでGetEnumerator()を1回呼び出し、反復ごとにMoveNext()を1回呼び出し、型チェックなどを行います。パフォーマンス測定に影響を与える可能性が最も高いのは、MoveNext()がO(N)回呼び出されるため、そのコストです。安いかもしれませんが、そうではないかもしれません。

「for」キーワードはより予測可能に見えますが、ほとんどの「for」ループ内には「collection [index]」のようなものが見つかります。これは単純な配列のインデックス操作のように見えますが、実際にはメソッド呼び出しであり、そのコストは、繰り返し処理するコレクションの性質に完全に依存します。おそらくそれは安いですが、そうではないかもしれません。

コレクションの基礎となる構造が本質的にリンクリストである場合、MoveNextは単純ですが、インデクサーはO(N)コストを持ち、 "for"ループの真のコストをO(N * N)にする可能性があります。


6

すべての言語構造には、使用する適切な時間と場所があります。C#言語に4つの別々の反復ステートメントがある理由があります -それぞれが特定の目的のために存在し、適切に使用されます。

上司と一緒に座って、forループに目的がある理由を合理的に説明することをお勧めします。時間があるfor反復ブロックがより明確により、アルゴリズム記述foreachの繰り返しは。これに該当する場合は、それらを使用するのが適切です。

私はあなたの上司にも指摘します-パフォーマンスは実用的ではなく、実用上の問題でもないはずです-それは簡潔で、意味のある、保守可能な方法でのアルゴリズムの表現の問題です。ループの再構築ではなく、アルゴリズムによる再設計とリファクタリングから実際のパフォーマンス上の利点が得られるため、このようなマイクロ最適化はパフォーマンス最適化のポイントを完全に逃します。

合理的な議論の後で、まだこの権威主義的見解がある場合、どのように進めるかはあなた次第です。個人的には、合理的な考えが推奨されない環境で働くのはうれしくなく、別の雇用主の下で別のポジションに移動することを検討します。ただし、混乱する前に話し合うことを強くお勧めします。単なる誤解があるだけかもしれません。


5

パフォーマンスに影響を与えるのはループの内部で行うことであり、実際のループ構造ではありません(ケースが自明ではない場合)。


5

かどうかforforeachポイントよりも本当に速いです。どちらを選択するかがパフォーマンスに大きな影響を与えることを真剣に疑っています。

アプリケーションを最適化する最良の方法は、実際のコードをプロファイリングすることです。これにより、最も多くの作業/時間を占めるメソッドが特定されます。最初にそれらを最適化します。それでもパフォーマンスが許容できない場合は、手順を繰り返します。

一般的なルールとして、マイクロ最適化は重要な利益を生み出すことはめったにないため、マイクロ最適化から離れることをお勧めします。唯一の例外は、識別されたホットパスを最適化する場合です(つまり、プロファイリングでいくつかの高度に使用されるメソッドが識別された場合、これらを広範囲に最適化することは理にかなっています)。


私が取り組んでいるプロジェクトで行う必要がある最適化の唯一の種類がマイクロ最適化である場合、私は幸せなキャンピングカーになるでしょう。悲しいことに、これは決してそうではありません。
Yannick Motton、2009

2
forはわずかに高速ですforeach。私はこの発言に真剣に反対します。それは根底にあるコレクションに完全に依存します。リンクリストクラスがインデクサーに整数パラメーターを提供する場合、O(n)であることが期待されているfor間、その上でループを使用するとO(n ^ 2)であることforeachが期待されます。
Mehrdad Afshari、

@Merhdad:実際、それは良い点です。リスト(配列)にインデックスを付ける通常のケースについて考えていました。それを反映するように言い直します。ありがとう。
Brian Rasmussen、

@Mehrdad Afshari:コレクションを整数でインデックス化すると、列挙するよりもはるかに遅くなる可能性があります。しかし、実際には、使用for インデクサールックアップをforeach単独で使用する場合比較しています。@Brian Rasmussenの答えは正しいと思います。コレクションでの使用は別として、for常によりわずかに速くなりforeachます。ただし、forさらに、コレクションの検索は常にforeachそれ自体よりも遅くなります。
ダニエル・プライデン2009

@Daniel:どちらも同じコードを生成するプレーン配列があるか、forステートメントを使用するときにインデクサーが関与しています。for整数の制御変数を使用した単純なループは、に匹敵するものforeachではないため、これは廃止されました。@Brianの意味を理解し、あなたの言うとおり正しいが、答えは誤解を招く可能性があります。再:あなたの最後のポイント:いいえ、実際には、forオーバーList<T>はまだより速いですforeach
Mehrdad Afshari、

4

2つはほぼ同じ方法で実行されます。両方を使用するコードを記述してから、ILを見せてください。比較可能な計算が表示されるはずです。つまり、パフォーマンスに違いはありません。


コンパイラーは、配列/ IListsなどで使用されるforeachループを認識し、それらをforループに変更します。
Callum Rogers、

3
それが大丈夫であることを理解できない証拠の行を彼に示し、それが大丈夫ではないことの彼の証拠を求めます。
cjk 2009

3

forには、実装するロジックが単純なので、foreachよりも高速です。


3

特定の速度最適化プロセスを実行しているのでない限り、コードの読み取りと保守が最も簡単な方法を使用すると思います。

いずれかのコレクションクラスのように、イテレータが既に設定されている場合、foreachは簡単なオプションです。そして、それがあなたが反復している整数の範囲であるなら、おそらくはよりクリーンです。



3

ほとんどの場合、実際には違いはありません。

通常、明示的な数値インデックスがない場合は常にforeachを使用する必要があり、実際に反復可能なコレクションがない場合(たとえば、上三角の2次元配列グリッドを反復する場合)は常に使用する必要があります。 。あなたが選択できるいくつかのケースがあります。

コードにマジックナンバーが現れ始めると、forループを維持するのが少し難しくなる可能性があると主張できます。forループが禁止されているからといって、forループを使用できず、コレクションを作成するか、ラムダを使用してサブコレクションを作成する必要があることに苛立つのは当然です。


3

forループのようなものの使用を完全に禁止するのは少し奇妙に思えます。

興味深い記事がありますここでは二つのループのパフォーマンスの違いの多くをカバーしています。

個人的にはforeachループよりもforeachの方が読みやすいと思いますが、forループの方が適切な場合は、手元のジョブに最適なものを使用し、foreachループを含めるために余分な長いコードを記述する必要はありません。


リンク先の記事の重要な引用:「...コレクションではない高性能のコードを記述する場合は、forループを使用します。コレクションの場合でも、foreachを使用すると便利に見えますが、それほど効率的ではありません。」
NickFitz 2009

3

より速くforeach繰り返すループを見つけました。以下のテスト結果をご覧ください。以下のコードでは、私は繰り返しサイズ100、10000と100000のが別々に使用し、時間を測定するループ。List arrayforforeach

ここに画像の説明を入力してください

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が次のループよりも速いことがわかりました。

  • 配列を使用したforeachループ。
  • リスト付きのforループ。
  • リスト付きのforeachループ。

以下のテスト結果とコードを参照してください。

ここに画像の説明を入力してください

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);
    }

3
これは非常に貧弱なテストです。a)反復回数が少なすぎて決定的な答えが得られないb)そのThread.Sleepは実際には1ミリ秒待機しません。ジョンスキートが彼の回答で行ったのと同じ方法を使用します。
jgauffin 14年

1
時間の99.99%は確実にthread.sleepで費やされます(これは、少なくともその時間までに戻らない限り、スレッドがどれだけ速く戻るかを保証するものではありません)。ループは非常に速く、スリープは非常に遅いので、前者をテストするために後者を使用しません。
Ronan Thibaudau 2015

3

あなたは本当に彼の頭をねじ込み、代わりにIQueryable .foreachクロージャーに行くことができます:

myList.ForEach(c => Console.WriteLine(c.ToString());

3
コード行をに置き換えますmyList.ForEach(Console.WriteLine)
Mehrdad Afshari、

2

私は誰もが2つの間の「巨大な」パフォーマンスの違いを見つけることを期待しません。

答えは、アクセスしようとしているコレクションのインデクサーアクセスの実装が速いか、IEnumeratorアクセス​​の実装が速いかによって異なります。IEnumeratorはインデクサーを頻繁に使用し、現在のインデックス位置のコピーを保持するだけなので、列挙子へのアクセスは、直接のインデックスアクセスと少なくとも同じかそれより遅いと予想されますが、それほどではありません。

もちろん、この答えは、コンパイラーが実装する可能性のある最適化を考慮していません。


C#コンパイラーは最適化をほとんど行わず、JITterに任せています。
ljs 2009年

まあ、JITterはコンパイラーですよね。
JohannesH

2

for-loopとforeach-loopは必ずしも同等ではないことに注意してください。リストの列挙子は、リストが変更されると例外をスローしますが、通常のforループで常に警告が表示されるとは限りません。リストが誤ったタイミングで変更されると、別の例外が発生する場合もあります。


リストがあなたの下から変化している場合、foreachループをサポートしている列挙子にそれを伝えることはできません。それはあなたに値を返した後に再びチェックすることはありません。
hoodaticus 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.