web-dev-qa-db-ja.com

C#-For vsForeach-パフォーマンスの大きな違い

与えられた配列でXより大きい最小の数を見つけるアルゴリズムにいくつかの最適化を行っていましたが、その後、奇妙な違いに遭遇しました。以下のコードでは、「ForeachUpper」は625msで終了し、「ForUpper」は数時間で終了すると思います(めちゃくちゃ遅い)。なぜそうなのか?

  class Teste
{
    public double Valor { get; set; }

    public Teste(double d)
    {
        Valor = d;
    }

    public override string ToString()
    {
        return "Teste: " + Valor;
    }
}

  private static IEnumerable<Teste> GetTeste(double total)
    {
        for (int i = 1; i <= total; i++)
        {
            yield return new Teste(i);
        }
    }
    static void Main(string[] args)
    {
        int total = 1000 * 1000*30 ;
        double test = total/2+.7;

        var ieTeste = GetTeste(total).ToList();


        Console.WriteLine("------------");

        ForeachUpper(ieTeste.Select(d=>d.Valor), test);
        Console.WriteLine("------------");
        ForUpper(ieTeste.Select(d => d.Valor), test);
        Console.Read();
    }

    private static void ForUpper(IEnumerable<double> bigList, double find)
    {
        var start1 = DateTime.Now;

        double uppper = 0;
        for (int i = 0; i < bigList.Count(); i++)
        {
            var toMatch = bigList.ElementAt(i);
            if (toMatch >= find)
            {
                uppper = toMatch;
                break;
            }
        }

        var end1 = (DateTime.Now - start1).TotalMilliseconds;

        Console.WriteLine(end1 + " = " + uppper);
    }

    private static void ForeachUpper(IEnumerable<double> bigList, double find)
    {
        var start1 = DateTime.Now;

        double upper = 0;
        foreach (var toMatch in bigList)
        {
            if (toMatch >= find)
            {
                upper = toMatch;
                break;
            }
        }

        var end1 = (DateTime.Now - start1).TotalMilliseconds;

        Console.WriteLine(end1 + " = " + upper);
    }

ありがとう

15
WoF_Angel

_IEnumerable<T>_はインデックスに登録できません。

forループのすべての反復で呼び出すCount()およびElementAt()拡張メソッドはO(n)です。カウントまたはn番目の要素を見つけるために、コレクションをループする必要があります。

道徳:あなたのコレクションの種類を知っています。

47
SLaks

この違いの理由は、forループがすべての反復でbigList.Count()を実行するためです。これは、Selectを実行し、完全な結果セットを反復するため、実際にはコストがかかります。

さらに、ElementAtを使用しています。これにより、選択が再度実行され、指定したインデックスまで繰り返されます。

13
Daniel Hilgarth