Performance 为什么AsQueryable对Linq这么慢?

Performance 为什么AsQueryable对Linq这么慢?,performance,linq,Performance,Linq,我在代码中遇到了一个相当愚蠢的性能问题。经过一个小的调查,我发现我用来转换我的泛型列表的AsQueryable方法将代码的速度降低了8000倍。 问题是,为什么会这样? 这是一个例子 class Program { static void Main(string[] args) { var c = new ContainerTest(); c.FillList(); var s = Environment.TickCount;

我在代码中遇到了一个相当愚蠢的性能问题。经过一个小的调查,我发现我用来转换我的泛型列表的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);
    }
}
类程序
{
静态void Main(字符串[]参数)
{
var c=新容器测试();
c、 填充列表();
var s=Environment.TickCount;
对于(int i=0;i<10000;++i)
{
c、 TestLinq(真);
}
var e=环境。滴答数;
WriteLine(“TestLinq AsQueryable-{0}”,e-s);
s=环境。滴答数;
对于(int i=0;i<10000;++i)
{
c、 TestLinq(假);
}
e=环境。滴答数;
WriteLine(“TestLinq作为列表-{0}”,e-s);
Console.WriteLine(“按enter键完成”);
Console.ReadLine();
}
}
类别容器测试
{
私有只读列表_List=new List();
私人保险公司;
公共无效填充列表()
{
_list.Clear();
对于(int i=0;i<10;++i)
{
_列表.添加(i);
}
_q=_list.AsQueryable();
}
公共元组TestLinq(bool-useAsQ)
{
var upperBorder=useAsQ?\u q.FirstOrDefault(i=>i>7):\u list.FirstOrDefault(i=>i>7);
var lowerBorder=useAsQ?_q.TakeWhile(i=>i<7).LastOrDefault():_list.TakeWhile(i=>i<7).LastOrDefault();
返回新元组(上边、下边);
}
}
UPD正如我所理解的,我必须尽可能避免AsQueryable方法(如果它不在容器的继承行中),因为我会立即遇到性能问题


“在黑暗中,当邪恶力量被提升的时候,要避开荒野”

至少也要使用LINQ和List

手动实现总是比LINQ更快

编辑


您知道这两个测试给出的结果并不相同,至少使用LINQ和List也是如此

手动实现总是比LINQ更快

编辑


您知道,这两个测试不会给出相同的结果,因为
AsQueryable
返回一个
IQueryable
,它为LINQ标准查询运算符提供了一组与用于
List
等操作的扩展方法完全不同的扩展方法


Queryable
集合意味着有一个RDBMS或类似的后备存储,当您调用
IQueryable.FirstOrDefault()
时,您正在构建一个不同的、更复杂的代码表达式树,而不是
List.FirstOrDefault()

因为
AsQueryable
返回一个
IQueryable
,它为LINQ标准查询运算符提供了一组与用于
列表等操作的扩展方法完全不同的扩展方法


Queryable
集合意味着有一个RDBMS或类似的后备存储,当您调用
IQueryable.FirstOrDefault()
时,您正在构建一个不同的、更复杂的代码表达式树,而不是
List.FirstOrDefault()

问题是,
IQueryable
Expression
作为在
Where()
/
FirstOrDefault()
调用中进行过滤的参数,而不仅仅是在简单的
IEnumerable
对应方法中使用的
Func
预编译委托

这意味着将有一个编译阶段将
表达式
转换为
委托
。而且这个费用相当高

现在你需要一个循环(就像我做的那样)?你会有麻烦的



附言:似乎是.NETcore/.NET5。不幸的是,我们的项目还没有完成…

只是面临着同样的问题

问题是,
IQueryable
Expression
作为在
Where()
/
FirstOrDefault()
调用中进行过滤的参数,而不仅仅是在简单的
IEnumerable
对应方法中使用的
Func
预编译委托

这意味着将有一个编译阶段将
表达式
转换为
委托
。而且这个费用相当高

现在你需要一个循环(就像我做的那样)?你会有麻烦的



附言:似乎是.NETcore/.NET5。不幸的是,我们的项目还没有实现…

使用LINQ与foreach的性能差异很小。这与8000倍的差距相差甚远。(坦白地说,这根本不是2倍的差异)@kirk woll,两个测试都没有给出结果,也没有使用相同的实现OK,只尝试TestLinq方法,一次使用just List,第二次使用List。AsQuerable使用LINQ over foreach的性能差异很小。这与8000倍的差距相差甚远。(坦白地说,两者相差不到2倍)@kirk woll,两个测试都没有给出结果,也没有使用相同的实现OK,只尝试TestLinq方法,一次使用just List,第二次使用List.asqueryable你知道两个测试给出的结果不一样,谢谢。您发现了一个bug:)为什么要在此集合上调用AsQueryable?这根本不适合这种用途。这会导致您的LINQ方法在以后希望构建一种不同类型的表达式树,而不仅仅是用于集合,而是用于“可查询”的东西。。。就像数据库表一样。因为最初我想将它与BindingList一起使用,我发现了性能问题。但对于LIst,它不应该影响性能,因为LIst是IQueryable,所以即使我调用AsQueryable(),它也必须以IQueryable的形式直接返回LIst->linq必须以表达式树->fast的形式执行。但它不是。这张单子不可靠,我的错。你知道两次测试的结果不一样,谢谢。你发现了一个bug:)为什么