【发布时间】:2013-04-12 02:49:09
【问题描述】:
查看我今天使用性能分析器处理的 web 应用程序的一部分。我以为是 Union 导致了一些延迟,但发现了其他令人惊讶的结果。
减速的原因之一似乎是 FirstOrDefault。
这是一个非常简单的 LINQ 查询,如下所示:
foreach(Report r in reports)
IDTOStudy study = studies.FirstOrDefault(s => s.StudyID == r.StudyID);
我创建了一个小方法来复制我认为 FirstOrDefault 正在做的行为。
private IDTOStudy GetMatchingStudy(Report report, IList<IDTOStudy> studies)
{
foreach (var study in studies)
if (study.StudyID == report.StudyID)
return study;
return null;
}
这个方法替换了 FirstOrDefault 看起来像这样:
foreach(Report r in reports)
IDTOStudy study = GetMatchingStudy(r, studies);
查看使用性能分析器运行的新代码显示,FirstOrDefault 的完成时间是我的新方法的两倍。看到这真是令人震惊。
FirstOrDefault() 查询一定是我做错了什么。它是什么?
FirstOrDefault() 是否完成整个查询然后获取第一个元素?
如何加快速度并使用FirstOrDefault()?
编辑 1:
我注意到的另外一点是,分析器说我在这两种实现中都用尽了我的 CPU。这也是我不关心和没想到的事情。我添加的附加方法并没有减少该峰值,只是将其持续时间缩短了一半。
编辑 3:
将研究结果放入字典极大地缩短了运行时间。这肯定是提交代码的外观。虽然没有回答关于 FirstOrDefault 的问题。
编辑 2:
这是在一个简单的控制台应用程序中请求的示例代码。我的运行仍然表明,在大多数情况下,FirstOrDefault 需要更长的时间。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Diagnostics;
namespace TestCode
{
public class Program
{
public List<IntHolder> list;
public static void Main(string[] args)
{
var prog = new Program();
prog.list = new List<IntHolder>();
prog.Add50000Items();
prog.list.Add(new IntHolder() { Num = 12345 });
prog.Add50000Items();
var stopwatch = new Stopwatch();
stopwatch.Start();
prog.list.FirstOrDefault(n => n.Num == 12345);
stopwatch.Stop();
Console.WriteLine("First run took: " + stopwatch.ElapsedTicks);
var lookingFor = new IntHolder() { Num = 12345 };
stopwatch.Reset();
stopwatch.Start();
prog.GetMatching(lookingFor);
stopwatch.Stop();
Console.WriteLine("Second run took: " + stopwatch.ElapsedTicks);
Console.ReadLine();
}
public void Add50000Items()
{
var rand = new Random();
for (int i = 0; i < 50000; i++)
list.Add(new IntHolder() { Num = rand.Next(100000) });
}
public IntHolder GetMatching(IntHolder num)
{
foreach (var number in list)
if (number.Num == num.Num)
return number;
return null;
}
}
public class IntHolder
{
public int Num { get; set; }
}
}
【问题讨论】:
-
如果您使用的是 Linq to SQL 或 EF,那么
FirstOrDefault()应该只生成TOP 1查询 -
什么是研究,它是一个 orm 对象(例如来自实体框架或类似的数据库集)?
-
我的第一直觉是错误在于您如何分析代码,而不是它的执行方式。您能否提供一个我们可以运行的示例程序来演示
FirstOrDefault与您在相同模式下的自定义方法之间的区别? -
LINQ-to-objects 比等效的手写代码慢 2 倍是标准的。额外的成本主要是由于额外的间接调用(委托、接口)
-
为什么不使用适当的数据结构来进行字典等查找?您的两种算法都是 O(reports.Count*studies.Count)。使用适当的数据结构,这将变为 O(reports.Count)