LINQ性能 - 延迟v/s立即执行

san*_*247 3 .net linq performance

我已经看到,如果LINQ to Objects查询被强制立即执行使用.ToArray(),但有时不能理解为什么,则可以显着提高它们的性能.例如,在下面的示例中,函数的执行Deferred()比函数慢得多Immediate(),并且随着值的增加呈指数级增长limit(也许它在两个函数中都是指数函数,但是执行时间Immediate()太短,我无法明确地说).

public void Deferred()
{
    var all = Range(limit);
    var even = from e in EvenRange(limit) where all.Contains(e) select e;
    var odd = from o in OddRange(limit) where !even.Contains(o) select o;

    var query = from q in odd select q;

    foreach(var i in query) { var j = i+1; }
}

public void Immediate()
{
    var all = Range(limit);
    var even = (from e in EvenRange(limit) where all.Contains(e) select e)
        .ToArray();
    var odd = (from o in OddRange(limit) where !even.Contains(o) select o)
        .ToArray();

    var query = (from q in odd select q).ToArray();

    foreach(var i in query) { var j = i+1; }
}

public static IEnumerable<int> OddRange(int stop)
{
    for (int i = 1; i < stop; i+=2) yield return i;
}

public static IEnumerable<int> EvenRange(int stop)
{
    for (int i = 2; i < stop; i+=2) yield return i;
}

public static IEnumerable<int> Range(int stop)
{
    for (int i = 0; i < stop; ++i) yield return i;
}
Run Code Online (Sandbox Code Playgroud)

是什么导致这种放缓?它只是编译器生成的额外代码,每次访问可枚举时都必须运行,或者Deferred()还会进入一些额外的循环,改变查询的"大O"复杂性?

em7*_*m70 6

据我所知,IEnumerable不会缓存结果,因此涉及even.Contains(必须检查所有偶数元素)的子句会强制重新枚举偶数,从而显示您注意到的性能行为.即使在数组或列表中进行转换也应该在枚举奇数时显示出良好的性能.其他.NET语言(如F#)包含缓存枚举结果的方法(如果您有兴趣,可以查看F#的Seq.cache方法)