WoF_Angel
WoF_Angel

Reputation: 2581

C# - For vs Foreach - Huge performance difference

i was making some optimizations to an algorithm that finds the smallest number that is bigger than X, in a given array, but then a i stumbled on a strange difference. On the code bellow, the "ForeachUpper" ends in 625ms, and the "ForUpper" ends in, i believe, a few hours (insanely slower). Why so?

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

Thanks

Upvotes: 16

Views: 24479

Answers (2)

SLaks
SLaks

Reputation: 887275

IEnumerable<T> is not indexable.

The Count() and ElementAt() extension methods that you call in every iteration of your for loop are O(n); they need to loop through the collection to find the count or the nth element.

Moral: Know thy collection types.

Upvotes: 55

Daniel Hilgarth
Daniel Hilgarth

Reputation: 174299

The reason for this difference is that your for loop will execute bigList.Count() at every iteration. This is really costly in your case, because it will execute the Select and iterate the complete result set.

Furthermore, you are using ElementAt which again executes the select and iterates it up to the index you provided.

Upvotes: 14

Related Questions