2

このことを考慮:

必要条件:

//The alphabet from a-z
List<char> letterRange = Enumerable.Range('a', 'z' - 'a' + 1)
.Select(i => (Char)i).ToList(); //97 - 122 + 1 = 26 letters/iterations

標準のforeach:

foreach (var range in letterRange)
{
    Console.Write(range + ",");
}
Console.Write("\n");

組み込みのforeach:

letterRange.ForEach(range => Console.Write(range + ",")); //delegate(char range) works as well
Console.Write("\n");

私はそれらを互いにタイミングを合わせてみましたが、組み込みのforeachは最大2倍高速で、かなりのようです。

グーグルで検索しましたが、答えが見つからないようです。

また、次の点についても説明します。.NETでは、「for」または「foreach」のどちらのループが高速に実行されますか?

for (int i = 0; i < letterRange.Count; i++)
{
    Console.Write(letterRange[i] + ",");
}
Console.Write("\n");

私が知る限り、標準のforeachよりも速く実行されるようには動作しません。

4

3 に答える 3

17

あなたのベンチマークには欠陥があると思います。Console.Writeは I/O バウンド タスクであり、ベンチマークで最も時間がかかる部分です。これはマイクロベンチマークであり、正確な結果を得るには非常に慎重に行う必要があります。

ベンチマークは次のとおりです: http://diditwith.net/PermaLink,guid,506c0888-8c5f-40e5-9d39-a09e2ebf3a55.aspx (見た目は良いですが、自分で検証していません)。2015 年 8 月 14 日時点でリンクが切れているようです

于 2009-04-13T14:39:54.907 に答える
11

foreach ループに入ると、各項目を列挙します。その列挙により、反復ごとに 2 つのメソッド呼び出しが発生します。1 つは へIEnumerator<T>.MoveNext()、もう 1 つは へIEnumerator<T>.Currentです。これは 2 つのcallIL 命令です。

List<T>.ForEachAction<T>提供されたデリゲートが何であれ、反復ごとにメソッド呼び出しが 1 つしかないため、高速です。それが 1 つのcallvirtIL 命令です。callこれは、2 つの命令よりも大幅に高速です。

他の人が指摘したように、次のような IO バインド命令Console.WriteLine()はベンチマークを汚染します。シーケンスの要素を一緒に追加するなど、完全にメモリに限定できることを行います。

于 2009-04-13T14:45:50.727 に答える
2

これは、foreach メソッドが列挙子を使用していないためです。列挙子 (foreach) は、基本的な for ループよりも遅くなる傾向があります。

ForEach メソッドのコードは次のとおりです。

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}

違いがあると思いますが、あなたが指摘したほど大きいことに少し驚いています。列挙子のアプローチを使用すると、追加のオブジェクト作成が行われ、列挙子が無効化されないようにするための追加の手順が実行されます (コレクションが変更されます)。また、追加の関数呼び出し Current() を使用してメンバーを取得します。これらすべてが時間を追加します。

于 2009-04-13T14:39:52.987 に答える