2013-03-04 16 views
13

Dokonałem pewnych optymalizacji algorytmu, który znajdzie najmniejszą liczbę, która jest większa niż X, w danej tablicy, ale potem natknąłem się na dziwną różnicę. Na poniższym kodzie "ForeachUpper" kończy się 625ms, a "ForUpper" kończy się, jak sądzę, kilkoma godzinami (szalenie wolniej). Dlaczego tak?C# - For vs Foreach - Ogromna różnica wydajności

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

Dzięki

+0

wierzę, że jest to możliwe powielać Kliknij tutaj, aby zobaczyć [Duplikat] [1] [1]: http://stackoverflow.com/questions/44220/difference-between- Foreach-and-for-Loops-over-an -ienumerable-in-c-sharp – SpaceApple

+3

BTW, użyj klasy 'Stopwatch'. – SLaks

+0

dlaczego "przełamujesz" z "jeśli"? – Ofiris

Odpowiedz

43

IEnumerable<T> nie można indeksować.

W Count() i ElementAt() rozszerzenie metod, które nazywamy w każdej iteracji pętli swojej for O (n); muszą przechodzić przez kolekcję, aby znaleźć liczbę lub n-ty element.

Moral: Znaj swoje typy kolekcji.

+9

+1 dla * moral * sekcja –

+0

Regardign 'Count()' warto zauważyć, że zależy to od faktycznego typu kolekcji, jeśli implementuje ICollection z właściwością Count 'Count()' użyje jej – sll

+1

@ll: To prawda dla ' ElementAt' też. Duże różnice znikną, jeśli OP doda ".ToArray()" po "Select". –

12

Przyczyną tej różnicy jest to, że pętla for wykona przy każdej iteracji. Jest to bardzo kosztowne w twoim przypadku, ponieważ spowoduje wykonanie Select i iteruje kompletny zestaw wyników.

Ponadto używasz ElementAt, który ponownie wykonuje wybieranie i iteruje je do podanego indeksu.

Powiązane problemy