20

これは、この優れた質問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>と比較して、処理のオーバーヘッドが (もしあれば) 少ないはずですが、別のコレクションを返す必要があります。しかし、それでもはるかに優れたパフォーマンスを発揮します。タイプに比べてオーバーヘッドがあるかもしれませんが、とにかく既存のリストに作用します! さらに、あるメソッドが既存の .NET メソッドよりも高速に動作するのを見るのはちょっと面白くありません。OrderByList<T>personsIOrderedEnumerable<T>OrderByList<T>IEnumerable<T>SortLinq

元の質問のすべての回答は、オーバーヘッドがあると思われるものと比較SortOrderBy.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;
        }
    });
}

Sort4000 ~ 5000 ミリ秒で実行され、5000 ミリ秒をOrderByわずかに上回って実行されました。だから確かに私の結論は間違っていました。コレクションの列挙を開始すると、どちらも同等の条件で実行されました。OrderBy私はanydayの構文を好みます:)

編集 2:これはthis oneの正確な複製であることがわかりました。しかし、順序付けについてではなく、一般的な遅延実行に関するより興味深い質問があります。

4

3 に答える 3

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が完全に実行され、実際に結果が順序付けられるためです。

于 2012-11-01T16:34:26.630 に答える
12

OrderBy()は、ほとんどの LINQ メソッドと同様に、遅延実行を使用します。

結果を列挙するまで、実際には何もしません。

そのパフォーマンスを適切に測定するには、 を呼び出すことができます.OrderBy(...).Count()

于 2012-11-01T16:34:08.437 に答える
2

OrderBy()ソートされたリストを作成しません。

IEnumerable オブジェクトを作成し、列挙すると並べ替えられたリストを生成します。リストを列挙するまで、実際の並べ替えは行われません。

于 2012-11-01T19:39:36.407 に答える