简体   繁体   中英

Why is LINQ faster in this example

I wrote the following to test the performance of using foreach vs LINQ :

private class Widget
{
    public string Name { get; set; }
}

static void Main(string[] args)
{
    List<Widget> widgets = new List<Widget>();
    int found = 0;

    for (int i = 0; i <= 500000 - 1; i++)
        widgets.Add(new Widget() { Name = Guid.NewGuid().ToString() });

    DateTime starttime = DateTime.Now;

    foreach (Widget w in widgets)
    {
        if (w.Name.StartsWith("4"))
            found += 1;
    }

    Console.WriteLine(found + " - " + DateTime.Now.Subtract(starttime).Milliseconds + " ms");

    starttime = DateTime.Now;
    found = widgets.Where(a => a.Name.StartsWith("4")).Count();

    Console.WriteLine(found + " - " + DateTime.Now.Subtract(starttime).Milliseconds + " ms");

    Console.ReadLine();
}

I get something like following output:

31160 - 116ms
31160 - 95 ms

In every run, LINQ outperforms foreach by around 20%. It was my understanding that the LINQ extension methods used standard c# under the covers.

So why is LINQ faster in this case?

EDIT:

So I changed my code to use stopwatch instead of datetime and still get the same results. If I run the LINQ query first then my results show LINQ to be about 20% slower then foreach. This has to be some sort of JIT warmnup issue. My question is how do I compensate for JIT warmup in my test case?

It's because you do not have a warmup. If you reverse your cases you will get excatly the opposit result:

31272 - 110ms
31272 - 80 ms

Start adding a warmup and use a stopwatch for better timing.

Running the test with warmup:

        //WARM UP:
        widgets.Where(a => a.Name.StartsWith("4")).Count();

        foreach (Widget w in widgets)
        {
            if (w.Name.StartsWith("4"))
                found += 1;
        }

        //RUN Test
        Stopwatch stopwatch1 = new Stopwatch();
        stopwatch1.Start();

        found = widgets.Where(a => a.Name.StartsWith("4")).Count();
        stopwatch1.Stop();

        Console.WriteLine(found + " - " + stopwatch1.Elapsed);

        found = 0;
        Stopwatch stopwatch2 = new Stopwatch();
        stopwatch2.Start();

        foreach (Widget w in widgets)
        {
            if (w.Name.StartsWith("4"))
                found += 1;
        }
        stopwatch2.Stop();

        Console.WriteLine(found + " - " + stopwatch2.Elapsed);

result:

31039 - 00:00:00.0783508
31039 - 00:00:00.0766299

I did some profiling a while back, comparing the following:

  • LINQ to Objects with/without Regex

  • Lambda Expressions with/without Regex

  • Traditional iteration with/without Regex

What I found out was that LINQ, Lambda and Traditional iteration were pretty much the same always, but the real timing difference was in the Regex expressions. Only adding the Regex made the evaluation slower (a LOT slower). (Details here: http://www.midniteblog.com/?p=72 )

What you are seeing above is probably due to the fact that you are doing both tests in the same code block. Try commenting one out, timing it, then commenting the other out. Also, make sure you are running a release build, not in the debugger.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM