Производительность List.Sort IComparer

Я пытаюсь отсортировать пару массивов int (int[] a; int[] b;)

Если я использую Array.Sort(a,b), то производительность отличная.

Однако я бы предпочел использовать List‹> и загружать пары int в структуру. Я могу заставить это работать, используя Array.Sort() с перегрузкой, которая обеспечивает простой компаратор для структуры, но примерно в 4 раза медленнее, чем Array.Sort(a,b)

Это нормально?


person rob    schedule 30.05.2009    source источник
comment
(обратите внимание на отредактированный ответ, чтобы добавить показатели для IComparable‹T›)   -  person Marc Gravell    schedule 31.05.2009
comment
Я полагаю, что и List.Sort, и Array.Sort используют быструю сортировку, что означает, что причиной разницы в производительности должен быть способ доступа к элементам.   -  person Tamas Czinege    schedule 31.05.2009
comment
Ага. Оба используют быструю сортировку. List‹Int32›.Sort немного медленнее, чем для обычного массива, но не имеет ничего общего с разницей при сортировке пар значений.   -  person rob    schedule 31.05.2009


Ответы (1)


Звучит реалистично; вы вводите больше сложности (больше поиска и т. д., больше виртуальных методов, больше проверок диапазона), поэтому это может только замедлиться (доступ к массиву очень прямой и очень быстрый).

Похоже, вы можете получить это немного быстрее (но не так быстро, как массив), реализуя IComparer<T> вместо подхода делегата; (редактировать) и еще раз быстрее, используя IComparable<T>:

Array.Sort: 2241ms
List.Sort (delegate): 8714ms
List.Sort (interface): 6976ms
List.Sort (comparable): 5456ms

С кодом:

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

struct MyStruct : IComparable<MyStruct>
{
    private readonly int key, value;
    public int Key { get { return key; } }
    public int Value { get { return value; } }
    public MyStruct(int key, int value)
    {
        this.key = key;
        this.value = value;
    }
    public int CompareTo(MyStruct other)
    {
        return key.CompareTo(other.key);
    }
}
static class Program
{
    static void Main()
    {
        const int SIZE = 10000000;
        int[] a = new int[SIZE], b = new int[SIZE];
        Random rand = new Random();
        for(int i = 0 ; i < SIZE ; i++) {
            a[i] = rand.Next();
            b[i] = i;
        }
        var list = new List<MyStruct>(SIZE);
        for (int i = 0; i < SIZE; i++)
        {
            list.Add(new MyStruct(a[i], b[i]));
        }
        var list2 = new List<MyStruct>(list);
        var list3 = new List<MyStruct>(list);

        var watch = Stopwatch.StartNew();
        Array.Sort(a, b);
        watch.Stop();
        Console.WriteLine("Array.Sort: " + watch.ElapsedMilliseconds + "ms");

        watch = Stopwatch.StartNew();
        list.Sort((x, y) => x.Key.CompareTo(y.Key));
        watch.Stop();
        Console.WriteLine("List.Sort (delegate): " + watch.ElapsedMilliseconds + "ms");

        watch = Stopwatch.StartNew();
        list2.Sort(MyComparer.Default);
        watch.Stop();
        Console.WriteLine("List.Sort (interface): " + watch.ElapsedMilliseconds + "ms");

        watch = Stopwatch.StartNew();
        list3.Sort();
        watch.Stop();
        Console.WriteLine("List.Sort (comparable): " + watch.ElapsedMilliseconds + "ms");
    }
    sealed class MyComparer : IComparer<MyStruct>
    {
        private MyComparer() { }
        public static readonly MyComparer Default = new MyComparer();
        public int Compare(MyStruct x, MyStruct y)
        {
            return x.Key.CompareTo(y.Key);
        }
    }
}
person Marc Gravell    schedule 30.05.2009
comment
Ваше здоровье. Спасибо, Марк. Не уверен, что смогу жить с потерей производительности, поэтому, вероятно, напишу что-то вроде класса List‹T1,T2›, который может использовать Array.Sort за кулисами. - person rob; 31.05.2009
comment
Хитрость заключается в том, чтобы использовать IComparable‹T›, а не просто получить от неуниверсального IComparable. Таким образом, при сравнении не нужно определять тип из Object. - person fmuecke; 12.03.2012
comment
@fmuecke во многих случаях дженерики не очень полезны (особенно все, что связано с привязкой или отражением данных); однако в приведенном здесь примере он использует общий API... - person Marc Gravell; 12.03.2012