![](/img/trans.png)
[英]Why is List<>.OrderBy LINQ faster than IComparable+List<>.Sort in Debug mode?
[英]Why is OrderBy which returns IOrderedEnumerable<T> much faster than Sort?
這是這個出色的問題C#Sort和OrderBy比較的后續。 我將使用相同的示例:
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>
,因此與OrderBy
相比,它在處理上的開銷(如果有的話)應該更少List<T>
在本例persons
),但要返回另一個集合IOrderedEnumerable<T>
但是OrderBy
性能仍然要好得多。 與IEnumerable<T>
類型相比, List<T>
可能具有一定的開銷,但是無論如何, Sort
都將對現有列表起作用! 此外,我不覺得看到Linq
方法比現有的.NET方法運行得更快而感到高興。
原始問題中的所有答案都將“ Sort
與“ 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;
}
});
}
Sort
在OrderBy
運行,而OrderBy
在5000ms以上運行。 所以確實我的結論是錯誤的。 一旦我開始枚舉集合,他們兩個人的表現就相等。 我更喜歡OrderBy
的語法:)
在這種情況下, OrderBy
快得多,因為您實際上沒有執行它。
在您枚舉結果之前,查詢將被推遲 ,因此它從未真正進行過排序。 在實際枚舉結果之前, IOrderedEnumerable<T>
不會處理輸入,也不執行任何形式的排序。
嘗試將基准更改為:
BenchMark(persons => people = persons.OrderBy(n => n.Name).Count());
Count()
調用將強制排序實際發生(因為它需要枚舉IOrderedEnumerable<T>
來生成一個計數),這應該使您的時間顯着平衡。
大多數LINQ擴展方法都是以這種方式工作的-在您枚舉它們之前(通過Count()
,調用ToList()
或僅在常規的foreach
循環中使用它們等),它們的影響可以忽略不計,因為它們實際上沒有做任何事情除了構建可枚舉對象外,直接進行其他操作。 其他基准與OrderBy(...).ToList()
相比的原因是,添加ToList()
強制OrderBy
完全執行並實際對結果進行排序。
與大多數LINQ方法一樣, OrderBy()
使用延遲執行。
在枚舉其結果之前,它實際上不會做任何事情。
要正確衡量其性能,可以調用.OrderBy(...).Count()
。
OrderBy()
不會創建排序列表。
它創建一個IEnumerable對象,當您枚舉該對象時,將生成一個排序列表。 枚舉列表之前,不會進行實際排序。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.