为什么LINQ在这个例子中更快?

本文关键字:LINQ 为什么 | 更新日期: 2023-09-27 18:18:48

我写了下面的代码来测试使用foreachLINQ的性能:

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

得到如下输出:

<>之前31160 - 116ms31160 - 95毫秒之前

在每次运行中,LINQ的性能都比foreach高出20%左右。这是我的理解,LINQ扩展方法使用标准的c#在幕后。

那么为什么在这种情况下LINQ更快呢?

编辑:

所以我改变了我的代码使用秒表而不是日期时间,仍然得到相同的结果。如果我先运行LINQ查询,那么我的结果显示LINQ比foreach慢20%左右。这一定是某种JIT预热问题。我的问题是如何在我的测试用例中补偿JIT预热?

为什么LINQ在这个例子中更快?

这是因为你没有热身。如果你颠倒大小写,你会得到完全相反的结果:

31272 - 110ms
31272 - 80 ms

开始热身,并使用秒表更好地计时。

运行带有预热的测试:

        //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);
结果:

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

我之前做了一些分析,比较了以下内容:

  • LINQ to object with/without Regex

  • 带/不带Regex的Lambda表达式

  • 使用/不使用Regex的传统迭代

我发现LINQ, Lambda和Traditional迭代几乎总是一样的,但真正的时间差异是在Regex表达式中。只添加Regex会使计算变慢(慢了很多)。(详见http://www.midniteblog.com/?p=72)

你在上面看到的可能是由于你在同一个代码块中做了两个测试。试着注释掉一个,计时,然后注释掉另一个。此外,请确保您正在运行一个发布版本,而不是在调试器中。