这是我今天晚上设置的测试。这是为了证明一些不同的东西,但结果并不完全像我预期的那样。
我在IQueryable上运行了一个有10000个随机查询的测试,在测试中我发现如果我在列表上做同样的事情,我的测试速度会快20倍。
见下文。我的CarBrandManager.GetList最初返回一个IQueryable,但现在我首先发出一个ToList(),然后它就快多了。
有人能告诉我为什么我看到了这么大的差异吗?
var sw = new Stopwatch();
sw.Start();
int queries = 10000;
//IQueryable<Model.CarBrand> carBrands = CarBrandManager.GetList(context);
List<Model.CarBrand> carBrands = CarBrandManager.GetList(context).ToList();
Random random = new Random();
int randomChar = 65;
for (int i = 0; i < queries; i++)
{
randomChar = random.Next(65, 90);
Model.CarBrand carBrand = carBrands.Where(x => x.Name.StartsWith(((char)randomChar).ToString())).FirstOrDefault();
}
sw.Stop();
lblStopWatch.Text = String.Format("Queries: {0} Elapsed ticks: {1}", queries, sw.ElapsedTicks);发布于 2012-10-26 06:21:51
这里可能有两个问题在起作用。首先:除了知道GetList(context)实现了IQueryable之外,还不清楚它返回了什么类型的集合。这意味着当您评估结果时,很可能是创建一个SQL查询,将该查询发送到数据库,并将结果物化到对象中。或者它可能正在解析XML文件。或下载RSS源或调用因特网上的OData端点。这显然比简单地在内存中过滤短列表要花费更多的时间。(毕竟,到底有多少个汽车品牌呢?)
但是让我们假设它返回的实现实际上是一个List,因此您测试的唯一区别是它是转换为IEnumerable还是IQueryable。将Enumerable类扩展方法上的方法签名与Queryable上的方法签名进行比较。当您将列表视为IQueryable时,您传入的是需要计算的Expressions,而不仅仅是可以直接运行的Funcs。
当您使用像Entity Framework这样的自定义LINQ提供程序时,这使框架能够评估实际的表达式树,并从它们生成SQL查询和物化计划。然而,LINQ to Objects只想计算内存中的lambda表达式,所以它要么使用反射,要么将表达式编译成Func,这两种方法都会对性能产生很大的影响。
您可能只想对结果集调用.ToList()或.AsEnumerable(),以强制它使用Funcs,但从information hiding的角度来看,这将是一个错误。您将假设您知道从GetList(context)方法返回的数据是某种内存中的对象。目前可能是这样,也可能不是。无论如何,它不是为GetList(context)方法定义的约定的一部分,因此您不能假设它总是这样。您必须假设您得到的类型很可能是您可以查询的类型。尽管目前可能只有十几个汽车品牌可供搜索,但有可能有一天会有数千个(我在这里说的是编程实践,并不一定是说汽车行业就是这样)。因此,你不应该假设下载整个汽车列表并在内存中过滤它们总是更快,即使现在恰好是这种情况。
如果CarBrandManager.GetList(context)可能返回由自定义LINQ提供程序(如实体框架集合)支持的对象,那么您可能希望将数据转换为IQueryable:即使您的基准测试显示使用list快20倍,但这种差异非常小,以至于没有用户能够区分。有一天,通过调用.Where().Take().Skip()并只从数据存储中加载真正需要的数据,您可能会看到几个数量级的性能提升,而如果您立即调用.ToList(),则最终会将整个表加载到系统内存中。
但是,如果您知道CarBrandManager.GetList(context)总是返回内存中的列表(顾名思义),那么应该将其更改为返回IEnumerable<Model.CarBrand>而不是IQueryable<Model.CarBrand>。或者,如果你使用的是.NET 4.5,可能是IReadOnlyList<Model.CarBrand>或IReadOnlyCollection<Model.CarBrand>,这取决于你愿意迫使你的CarManager遵守什么协议。
https://stackoverflow.com/questions/13078208
复制相似问题