的FindAll VS拉制法(FindAll vs Where extension-method)

2019-07-21 06:33发布

我只是想知道,如果一个“的FindAll”会比“去哪儿” extentionMethod为什么快?

例如:

myList.FindAll(item=> item.category == 5);

要么

myList.Where(item=> item.category == 5);

哪个更好 ?

Answer 1:

那么, FindAll复制匹配的元素,以一个新的列表,而Where只返回一个懒洋洋地评估序列-没有复制是必需的。

因此,我期望Where会比稍快FindAll即使所得序列充分评估-当然懒惰的评价策略Where意味着,如果你只是看(说)的第一场比赛,也不会需要检查列表的其余部分。 (马修指出,有一个在保持状态机工作Where然而,这只会有一个固定的存储成本-而构建一个新的列表可能需要多个阵列分配等)

基本上, FindAll(predicate)接近Where(predicate).ToList()而不是仅仅Where(predicate)

只是反应更马修的回答了一下,我不认为他测试了相当不够彻底。 他断言恰好挑一半的项目。 下面是该测试相同的名单,但有三个不同的谓词简短而完整的程序 - 一个挑选任何项目,一个挑选的所有项目,以及一个挑选其中的一半。 在每种情况下运行测试五十次获得更长的时间。

我使用Count()以确保在Where结果完全评估。 结果表明,收集大约一半的结果,两者并驾齐驱。 收集没有结果, FindAll获胜。 收集所有的结果, Where获胜。 我觉得这是耐人寻味:随着越来越多的比赛找到了所有的解决方案变得更慢: FindAll有更多的拷贝做的, Where有,而不是返回的内循环正好匹配值MoveNext()执行。 然而, FindAll获得速度比慢Where呢,所以失去了早期的领先优势。 很有意思。

结果:

FindAll: All: 11994
Where: All: 8176
FindAll: Half: 6887
Where: Half: 6844
FindAll: None: 3253
Where: None: 4891

(编译与/ O + /调试 - 和通过命令行,.NET 3.5运行)。

码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

class Test
{
    static List<int> ints = Enumerable.Range(0, 10000000).ToList();

    static void Main(string[] args)
    {
        Benchmark("All", i => i >= 0); // Match all
        Benchmark("Half", i => i % 2 == 0); // Match half
        Benchmark("None", i => i < 0); // Match none
    }

    static void Benchmark(string name, Predicate<int> predicate)
    {
        // We could just use new Func<int, bool>(predicate) but that
        // would create one delegate wrapping another.
        Func<int, bool> func = (Func<int, bool>) 
            Delegate.CreateDelegate(typeof(Func<int, bool>), predicate.Target,
                                    predicate.Method);
        Benchmark("FindAll: " + name, () => ints.FindAll(predicate));
        Benchmark("Where: " + name, () => ints.Where(func).Count());
    }

    static void Benchmark(string name, Action action)
    {
        GC.Collect();
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < 50; i++)
        {
            action();
        }
        sw.Stop();
        Console.WriteLine("{0}: {1}", name, sw.ElapsedMilliseconds);
    }
}


Answer 2:

怎么样,我们测试,而不是猜测? 耻辱看到错误的答案脱身。

var ints = Enumerable.Range(0, 10000000).ToList();
var sw1 = Stopwatch.StartNew();
var findall = ints.FindAll(i => i % 2 == 0);
sw1.Stop();

var sw2 = Stopwatch.StartNew();
var where = ints.Where(i => i % 2 == 0).ToList();
sw2.Stop();

Console.WriteLine("sw1: {0}", sw1.ElapsedTicks);
Console.WriteLine("sw2: {0}", sw2.ElapsedTicks);
/*
Debug
sw1: 1149856
sw2: 1652284

Release
sw1: 532194
sw2: 1016524
*/

编辑:

即使我把从上面的代码

var findall = ints.FindAll(i => i % 2 == 0);
...
var where = ints.Where(i => i % 2 == 0).ToList();

... 至 ...

var findall = ints.FindAll(i => i % 2 == 0).Count;
...
var where = ints.Where(i => i % 2 == 0).Count();

我得到这些结果

/*
Debug
sw1: 1250409
sw2: 1267016

Release
sw1: 539536
sw2: 600361
*/

编辑2.0 ...

如果你想如果的FindAll()当前列表的子集,最快的方法列表。 这样做的原因是简单的。 该实例的FindAll方法使用在目前的名单,而不是枚举状态机索引。 何处()扩展方法是一个外部呼叫到使用枚举不同的类。 如果从每个节点列表中的步骤到下一个节点,你将不得不调用覆盖下的MoveNext()方法。 正如你可以从上面的例子中看到它甚至更快地使用索引条目来创建一个新的列表(即指向原来的物品,所以内存膨胀将是最小的),甚至只得到过滤后的项目数。

现在,如果你要早从枚举中止凡()方法可能会更快。 当然,如果你移动的早期中断逻辑来的FindAll()方法的谓词时将再次使用索引,而不是枚举。

现在有其他原因使用Where()语句(如其他LINQ方法的foreach块等等),但问题是,是的FindAll()快于凡()。 除非你不执行其中()答案似乎是肯定的。 (当比较苹果和苹果)

我不是说不要使用LINQ或。凡()方法。 他们为代码要简单得多阅读。 现在的问题是关于性能,而不是如何可以轻松阅读和理解的代码。 通过快速做这项工作的最快的方法是使用一个块步进每个索引和做任何逻辑,只要你想(甚至早退出)。 究其原因LINQ是如此之大,是becasue复杂的表达式树和改造你可以与他们得到的。 但是,使用迭代从。凡()方法去,虽然吨的代码,找出它的方式到内存中的statemachine是刚刚起步的下一个索引淘汰之列。 还应当指出的是,此.FindAll()方法是仅在implmented它对象有用(如Array和List)。

然而,更多...

for (int x = 0; x < 20; x++)
{
    var ints = Enumerable.Range(0, 10000000).ToList();
    var sw1 = Stopwatch.StartNew();
    var findall = ints.FindAll(i => i % 2 == 0).Count;
    sw1.Stop();

    var sw2 = Stopwatch.StartNew();
    var where = ints.AsEnumerable().Where(i => i % 2 == 0).Count();
    sw2.Stop();

    var sw4 = Stopwatch.StartNew();
    var cntForeach = 0;
    foreach (var item in ints)
        if (item % 2 == 0)
            cntForeach++; 
    sw4.Stop();

    Console.WriteLine("sw1: {0}", sw1.ElapsedTicks);
    Console.WriteLine("sw2: {0}", sw2.ElapsedTicks);
    Console.WriteLine("sw4: {0}", sw4.ElapsedTicks);
}


/* averaged results
sw1 575446.8
sw2 605954.05
sw3 394506.4
/*


Answer 3:

嗯,至少你可以尝试来衡量它。

静态Where方法使用迭代集团(实施yield关键字),这基本上意味着,执行将被推迟。 如果只比较调用论文两种方法,第一个会慢一些,因为它马上意味着整个集合将被重复。

但是,如果你包括你得到的结果完全重复,事情可能会有点不同。 我敢肯定的yield的解决方案是比较慢,因为它意味着所生成的状态机机制。 (见@Matthew anwser)



Answer 4:

我可以提供一些线索,但不知道哪一个更快。 的FindAll()被立即执行。 其中()是defferred执行。



Answer 5:

哪里是延迟执行的优点。 看到其中的差别,如果你有以下功能

BigSequence.FindAll( x =>  DoIt(x) ).First();
BigSequence.Where( x => DoIt(x) ).First();

的FindAll已经覆盖了完整的sequene,而凡在大多数序列将停止只要一个元素被发现枚举。

同样的影响将是一个使用任何(),取(),跳过(),等我不知道,但我想你会在已经延迟执行的所有功能都有巨大的优势



文章来源: FindAll vs Where extension-method