【问题标题】:Slow AsQueryable() and universal method for IQueryable and IEnumerable慢 AsQueryable() 和 IQueryable 和 IEnumerable 的通用方法
【发布时间】:2015-12-14 12:09:15
【问题描述】:

我想为 IQueryable 和 IEnumerable 使用一种方法:

public static IEnumerable<T> WhereEx<T>(this IEnumerable<T> query, Expression<Func<T, bool>> exp)
{
   return query.AsQueryable().Where(exp);
}

public static IEnumerable<Ship> GetShipsSome(this IEnumerable<Ship> query)
{
   return query.WhereEx(x => x.Id > 150);
}

使用: 使用 IQueryable:

var x = context.Set<Ships>().AsNoTracking().GetShipsSome(); //from DB

使用 IEnumerable:

var x = shipsInRam.GetShipsSome(); //from collection in ram(list)

但是如果我使用IEnumerable(从内存中的集合中获取),我的代码比IEnumerable.Where 慢,因为AsQueryable() 转换集合。如何优化我的代码?

修复:FirstOrDefault 而不是 Where

【问题讨论】:

  • queryIEnumerable 还是 IQueryable
  • 查询可以是IEnumerable和IQueryable
  • 你在做性能测试时通过了什么?
  • shipsInRam.AsQueryable().Where(exp); vs shipsInRam.Where(exp);
  • 你的shipsInRam绝对是实际上在内存中吗?你能试试shipsInRam.ToList().AsQueryable().Where(exp) vs shipsInRam.ToList().Where(exp)吗?

标签: c# entity-framework linq iqueryable


【解决方案1】:

阅读您的声明后,我不相信 - 所以我写了一个基准测试。

var list = Enumerable.Range(1, 1000000).ToList();

for(var i = 0; i < 100; i++)
{
    var warmupA = list.WhereEx(a => a > 500000).ToList();
    var warmupB = list.Where(a => a > 500000).ToList();;
    var warmupC = list.WhereExCompile(a => a > 500000).ToList();;
}

var sw = new Stopwatch();
sw.Start();

for(var i = 0; i < 100; i++)
{
    var wherexresult =  list.WhereEx(a => a > 500000).ToList();
}

sw.Stop();
var wherextime = sw.ElapsedMilliseconds;

sw = new Stopwatch();
sw.Start();

for(var i = 0; i < 100; i++)
{
    var whereresult =  list.Where(a => a > 500000).ToList();
}

sw.Stop();
var whertime = sw.ElapsedMilliseconds;

sw = new Stopwatch();
sw.Start();

for(var i = 0; i < 100; i++)
{
    var whereexcompileresult =  list.WhereExCompile(a => a < 500000).ToList();
}

sw.Stop();
var whereexcompiletime = sw.ElapsedMilliseconds;

wherextime.Dump();
whertime.Dump();
whereexcompiletime.Dump();

public static class a  {
    public static IEnumerable<T> WhereExCompile<T>(this IEnumerable<T> query, Expression<Func<T, bool>> exp)
    {
        return query.AsQueryable().Where(exp);
    }
    public static IEnumerable<T> WhereEx<T>(this IEnumerable<T> query, Expression<Func<T, bool>> exp)
    {
        return query.Where(exp.Compile());
    }
}

结果如下:

list.WhereEx(a => a > 500000) = 1320ms
list.Where(a => a > 500000) = 1572ms
list.WhereExCompile(a => a < 500000) = 1284ms

启用优化:

list.WhereEx(a => a > 500000) = 1285ms
list.Where(a => a > 500000) = 1372ms
list.WhereExCompile(a => a < 500000) = 1263ms

差异可以忽略不计(运行 100,000,000 次每次,每次耗时不到 1.5 秒)。如果有的话,您的方法比.Where()。那么你从哪里得到代码需要优化的想法呢?

【讨论】:

  • 先尝试或默认而不是 Where。并查询多个属性。
  • @GLeBaTi FirstOrDefault 甚至比 where 更好WhereExWhereExCompile 始终比“Where”快 3-4 倍。我不相信几个属性会对性能产生任何影响 - 尽管欢迎您使用基准代码并自己尝试。
  • 不知何故我变慢了,但时间很短。显然我需要减少通话次数。谢谢。
  • @GLeBaTi 我看了你的照片。我在基准测试中得到了类似的结果,但是它与我测试的并不完全相同。您可以尝试用allAsteroids.AsQueryable().Where(...).FirstOrDefault() 替换allAsteroids.AsQueryable().FirstOrDefault(...) 吗?对我来说,这种方式要快 2-3 倍。然而,我不知道为什么。
【解决方案2】:

您应该返回IQueryable&lt;T&gt; 而不是IEnumerable&lt;T&gt;,如果您获取记录只是为了向您的用户显示,并且不会更改任何这些记录,为了获得更好的性能,您可以使用AsNoTracking() 获取记录。

还有一点,你必须使用AsQueryable() 喜欢这个

return query.Where(exp).AsQueryable();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-05
    • 2012-06-23
    • 2011-07-28
    • 1970-01-01
    相关资源
    最近更新 更多