为什么OrderBy返回IOrderedEnumerable <t>比Sort?</t>快得多

时间:2012-11-01 16:33:05

标签: c# .net linq sorting collections

这是对这个优秀问题C# Sort and OrderBy comparison的跟进。我将使用相同的例子:

List<Person> persons = new List<Person>();
persons.Add(new Person("P005", "Janson"));
persons.Add(new Person("P002", "Aravind"));
persons.Add(new Person("P007", "Kazhal"));

争用的方法是:

persons.Sort((p1, p2) => string.Compare(p1.Name, p2.Name, true));
//and
persons.OrderBy(n => n.Name);

首先我要说的是,我知道没有任何重大的性能差异需要担心。但我很想知道为什么OrderBy的表现比Sort好得多。我正在使用@phoog在原始问题中发布的答案。

private void button1_Click(object sender, EventArgs e)
{
    IEnumerable<Person> people;

    BenchMark(persons => persons.Sort((p1, p2) => string.Compare(p1.Name, p2.Name, true)));

    BenchMark(persons => people = persons.OrderBy(n => n.Name));
}

private static Random randomSeed = new Random();
public static string RandomString(int size, bool lowerCase)
{
    var sb = new StringBuilder(size);
    int start = (lowerCase) ? 97 : 65;
    for (int i = 0; i < size; i++)
    {
        sb.Append((char)(26 * randomSeed.NextDouble() + start));
    }
    return sb.ToString();
}

private static void BenchMark(Action<List<Person>> action)
{
    List<Person> persons = new List<Person>();
    for (int i = 0; i < 10000; i++)
    {
        persons.Add(new Person("P" + i.ToString(), RandomString(5, true)));
    }
    List<Person> unsortedPersons = new List<Person>(persons);

    Stopwatch watch = new Stopwatch();
    for (int i = 0; i < 100; i++)
    {
        watch.Start();

        action(persons);

        watch.Stop();
        persons.Clear();
        persons.AddRange(unsortedPersons);
    }

    MessageBox.Show(watch.Elapsed.TotalMilliseconds.ToString());
}

结果:

Sort() => 3500 ~ 5000 ms
OrderBy() => 0.2 ~ 1.5 ms

虽然我最初测试的列表较小,但差异很大,但一旦收集的大小上升,它就变得越来越明显了。可能是我错过了理解.NET集合的关键,但我的想法是Sort作用于现有的List<T>,与{相比}处理时应该有更少的开销(如果每一个) {1}}作用于同一OrderBy(在我们的案例中为List<T>),但必须返回另一个集合IOrderedEnumerable<T>。但是persons仍然表现得更好。与OrderBy类型相比,List<T>可能会有一定的开销,但IEnumerable<T>无论如何都会对现有列表起作用!此外,我很难看到Sort方法比现有的.NET方法工作得更快。

原始问题中的所有答案都会将LinqSort进行比较,我认为这些答案会产生一些开销,因此或多或少会有相同的影响。

实施差异可能是什么?


编辑:好的,我学到了新东西。以下是我对延期执行的确认。

OrderBy.ToList

private void button1_Click(object sender, EventArgs e) { BenchMark(persons => { persons.Sort((p1, p2) => string.Compare(p1.Name, p2.Name, true)); foreach (var item in persons) { break; } }); BenchMark(persons => { IEnumerable<Person> people = persons.OrderBy(n => n.Name); foreach (var item in people) { break; } }); } 在4000 - 5000毫秒时运行,Sort在5000毫米以上运行。所以我的结论确实是错的。一旦我开始枚举集合,它们都以平等的条件执行。我更喜欢OrderBy任何一天的语法:)

编辑2:我刚发现这与this one完全相同。但这里有一个more interesting question about deferred execution in general,但不完全是排序。

3 个答案:

答案 0 :(得分:37)

在这种情况下,OrderBy要快得多,因为你实际上并没有执行它。

在您枚举结果之前,查询 deferred ,因此它实际上从未进行过排序。在您实际枚举结果之前,IOrderedEnumerable<T>不会处理输入并执行任何形式的排序。

尝试将基准更改为:

 BenchMark(persons => people = persons.OrderBy(n => n.Name).Count());

Count()调用将强制实际进行排序(因为它需要枚举IOrderedEnumerable<T>以生成计数),这会使您的计时显着均匀。

大多数LINQ扩展方法都是这样工作的 - 直到你枚举它们(通过Count(),调用ToList(),或者只是在正常的foreach循环中使用它们等),它们将具有可忽略不计的影响,因为除了构建可枚举之外,它们实际上并没有直接做任何事情。其他基准与OrderBy(...).ToList()比较的原因是ToList()的加法迫使OrderBy完全执行并实际排序结果。

答案 1 :(得分:12)

与大多数LINQ方法一样,

OrderBy()使用延迟执行。

在您枚举其结果之前,它实际上并没有做任何事情。

要正确衡量其效果,您可以拨打.OrderBy(...).Count()

答案 2 :(得分:2)

OrderBy()不会创建排序列表。

它创建一个IEnumerable对象,当您枚举它时,它会生成一个已排序的列表。在枚举列表之前,实际排序不会发生。

相关问题