Почему List <>. OrderBy LINQ быстрее, чем IComparable + List <>. Сортировка в режиме отладки?
Меня интересовало, будет ли быстрее сортировать мои классы с помощью LINQ или реализовать интерфейс IComparable и List.Sort. Я был очень удивлен, когда код LINQ был быстрее.
Чтобы выполнить тест, я сделал очень простой класс с не очень подходящим именем TestSort, реализующим IComparable.
class TestSort: IComparable<TestSort> {
private int age;
private string givenName;
public int Age {
get {
return age;
}
set {
age = value;
}
}
public string GivenName {
get {
return givenName;
}
set {
givenName = value;
}
}
public TestSort(int age, string name) {
this.age = age;
this.givenName = name;
}
public int CompareTo(TestSort other) {
return this.age.CompareTo(other.age);
}
}
Тогда простая программа для ее сортировки много раз - сортировка была намного дороже, чем копирование списка, поэтому эффект этого можно игнорировать.
class Program {
static void Main(string[] args) {
// Create the test data
string name = "Mr. Bob";
Random r = new Random();
var ts2 = new List<TestSort>();
for (int i = 0; i < 100; i++) {
ts2.Add(new TestSort(r.Next(), name));
}
DateTime start, end;
// Test List<>.Sort
start = DateTime.Now;
for (int i = 0; i < 100000; i++) {
var l = ts2.ToList();
l.Sort();
}
end = DateTime.Now;
Console.WriteLine("IComparable<T>: ");
Console.WriteLine((end - start).TotalMilliseconds);
// Test Linq OrderBy
start = DateTime.Now;
for (int i = 0; i < 100000; i++) {
var l = ts2.ToList();
l = l.OrderBy(item => item.Age).ToList();
}
end = DateTime.Now;
Console.WriteLine("\nLINQ: ");
Console.WriteLine((end - start).TotalMilliseconds);
Console.WriteLine("Finished.");
Console.ReadKey();
}
}
Я был очень удивлен, получив следующий вывод:
IComparable<T>:
2965.1696
LINQ:
2181.1248
Иногда LINQ будет ниже 2000, а иногда IComparable будет около 3000.
Когда я тестировал его с обычным List<Int>
, List.Sort
составлял 1/4 скорость LINQ, которая оставалась примерно в 2000 году.
Итак, почему LINQ только около 66% скорости обычной сортировки для моего класса? Я что-то не так с моей реализацией IComparable?
Update:
Я просто подумал попробовать сделать это в режиме выпуска, и да, результаты были разными:
IComparable<T>:
1593.0911
Linq:
1958.1119
Но мне все еще очень интересно узнать, почему IComparable работает медленнее в режиме отладки.
Ответы
Ответ 1
Если вы убедитесь, что перед началом измерения все JITed, вы можете получить разные результаты (я также рекомендую использовать класс Stopwatch
для измерения времени):
var ll = ts2.ToList();
ll.Sort();
ll.OrderBy(item => item.Age).ToList();
Согласно моим измерениям (после добавления вышеуказанного кода), IComparable всегда быстрее (даже при отладке).
Ответ 2
Сортировка использует unoptimized quicksort, которая имеет сложность n * n в худшем случае. Я не знаю, что использует orderby, но я знаю, что он не использует тот же метод, поскольку он устойчив, в отличие от array.sort
.
Ответ 3
Это может быть накладные расходы на вызов метода CompareTo
, который будет заменен встроенным при компиляции и запуском в режиме выпуска.
Ответ 4
Для меня я буду использовать Linq с IComparable (когда это самый или единственный способ сортировки). В этом случае элемент TestSort: IComparable<TestSort>
var sorted = ll.OrderBy(item => item); // This automatically used age to compare, as it defined in CompareTo
ll
может быть любым IEnumerable: List, Array и т.д.
Также в CompareTo
у вас может быть несколько способов сравнения... например, вы можете сделать что-то вроде этого:
public int CompareTo(TestSort other) {
return this.age != other.age ? this.age.CompareTo(other.age) : this.dateOfBirth.CompareTo(other.dateOfBirth);
}