【问题标题】:Why AsQueryable is so slow with Linq?为什么 AsQueryable 使用 Linq 这么慢?
【发布时间】:2023-04-02 16:21:01
【问题描述】:

我在代码中遇到了一个相当愚蠢的性能问题。经过一个小的调查,我发现我用来转换我的通用列表的 AsQueryable 方法将代码减慢了 8000 倍。 所以问题是,为什么会这样? 这是一个例子

class Program
{
    static void Main(string[] args)
    {
        var c = new ContainerTest();
        c.FillList();

        var s = Environment.TickCount;
        for (int i = 0; i < 10000; ++i)
        {
            c.TestLinq(true);
        }
        var e = Environment.TickCount;
        Console.WriteLine("TestLinq AsQueryable - {0}", e - s);

        s = Environment.TickCount;
        for (int i = 0; i < 10000; ++i)
        {
            c.TestLinq(false);
        }
        e = Environment.TickCount;
        Console.WriteLine("TestLinq as List - {0}", e - s);

        Console.WriteLine("Press enter to finish");
        Console.ReadLine();
    }
}

class ContainerTest
{
    private readonly List<int> _list = new List<int>();
    private IQueryable<int> _q; 

    public void FillList()
    {
        _list.Clear();
        for (int i = 0; i < 10; ++i)
        {
            _list.Add(i);
        }
        _q = _list.AsQueryable();
    }

    public Tuple<int, int> TestLinq(bool useAsQ)
    {
        var upperBorder = useAsQ ? _q.FirstOrDefault(i => i > 7) : _list.FirstOrDefault(i => i > 7);
        var lowerBorder = useAsQ ? _q.TakeWhile(i => i < 7).LastOrDefault() : _list.TakeWhile(i => i < 7).LastOrDefault();            

        return new Tuple<int, int>(upperBorder, lowerBorder);
    }
}

UPD 据我了解,我必须尽可能避免使用 AsQueryable 方法(如果它不在容器的继承行中),因为我会立即遇到性能问题

“在邪恶力量高涨的黑暗时刻避开荒野”

【问题讨论】:

  • 你知道这两个测试没有给出相同的结果
  • 呃,谢谢。你发现了一个错误:)
  • 你为什么在这个集合上调用 AsQueryable?这根本不适合这种用途。这会导致您的 LINQ 方法之后期望构建一种不同类型的表达式树,而不是用于简单的集合,而是用于“可查询”的东西......比如数据库表。
  • 因为最初我想将它与 BindingList 一起使用,但我发现了我的性能问题。但是对于 LIst 它不应该影响性能,因为 Listis IQueryable,所以即使我调用 AsQueryable() 它也必须直接将列表返回为 IQueryable -> linq 必须是执行为表达式树-> 快速。但这不是
  • 哦。列表不是 IQueryable,我的错。

标签: performance linq


【解决方案1】:

至少也将 LINQ 与 List 一起使用

手动实现总是比 LINQ 快

编辑

你知道这两个测试不会给出相同的结果

【讨论】:

  • 在 foreach 上使用 LINQ 的性能差异很小。它没有接近 8000 倍的差异。 (坦率地说,相差不到 2 倍)
  • @kirk woll,两个测试都没有给出结果并且不使用相同的实现
  • 好的,只试试TestLinq方法,第一次用List,第二次用List.AsQuerable
【解决方案2】:

因为AsQueryable 返回一个IQueryable,它具有一组完全不同的LINQ 标准查询运算符的扩展方法,与用于List 之类的扩展方法完全不同。

Queryable 集合旨在具有 RDBMS 或类似东西的后备存储,当您调用 IQueryable.FirstOrDefault() 而不是 List&lt;&gt;.FirstOrDefault() 时,您正在构建一个不同的、更复杂的代码表达式树。

【讨论】:

    【解决方案3】:

    刚刚遇到同样的问题。

    问题在于IQueryable&lt;T&gt;Expression&lt;Func&lt;T, Bool&gt;&gt; 作为Where()/FirstOrDefault() 调用中的过滤参数——而不仅仅是Func&lt;T, Bool&gt; 在简单IEnumerable 的对应方法中采用的预编译委托.

    这意味着将有一个编译阶段将Expression 转换为delegate。而且这要花很多钱。

    现在您需要循环使用它(就像我做的那样)?你会遇到一些麻烦...


    PS:似乎 .NET Core/.NET 5 improves this significantly。不幸的是,我们的项目还没有...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-05
      • 1970-01-01
      • 1970-01-01
      • 2013-02-28
      • 2021-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多