[英]Is this really a case when Linq is faster than a Foreach
如果您搜索的是Linq更快,則Foreach答案永遠不是foreach。 我還發現了另一個stackoverflow問題,其中問問題者未進行“熱身”,因此我在代碼中包含了“熱身”。
由於某些原因,我的代碼示例未按預期運行。 我在想我所做的是使no linq路徑循環兩次-第一次,一次總計。 當linq示例進行求和時,最后僅循環一次。 你怎么看? 我的測試有缺陷嗎?還是這是linq實際為我們帶來良好性能提升的情況?
public class NumberObject { public Int32 Number { get; set; } }
public IEnumerable<NumberObject> GetNumbersWithoutLambda()
{
IEnumerable<Int32> numberRange = Enumerable.Range(0,10);
List<NumberObject> numberList = new List<NumberObject>();
foreach (Int32 item in numberRange)
{
numberList.Add(new NumberObject() { Number = item });
}
return numberList;
}
public IEnumerable<NumberObject> GetNumbersWithLambda()
{
IEnumerable<Int32> numberRange = Enumerable.Range(0, 10);
IEnumerable<NumberObject> numbers = numberRange.
Select(number => new NumberObject() { Number = number });
return numbers;
}
private void runGetNumbers(Func<IEnumerable<NumberObject>> getNumbersFunction, Int32 numberOfTimesToRun)
{
for (int i = 0; i < numberOfTimesToRun; i++)
{
IEnumerable<NumberObject> numbers = getNumbersFunction();
//numbers.Count();
numbers.Sum(item => item.Number);
//numbers.Average(item => item.Number);
}
}
[TestMethod]
public void record_speed_of_GetNumbers()
{
Int32 numberOfTimes = 10000000;
Console.WriteLine("Doing warmup... " +
TimeMethod(() => runGetNumbers(GetNumbersWithLambda, numberOfTimes)));
Console.WriteLine("GetNumbersWithoutLambda: " +
TimeMethod(() => runGetNumbers(GetNumbersWithoutLambda, numberOfTimes)) + " milliseconds");
Console.WriteLine("GetNumbersWithLambda: " +
TimeMethod(() => runGetNumbers(GetNumbersWithLambda, numberOfTimes)) + " milliseconds");
}
static long TimeMethod(Action methodToTime)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
methodToTime();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
以下是測試的輸出:
正在做熱身... 7560
GetNumbersWithoutLambda: 14779毫秒
GetNumbersWithLambda: 7626毫秒
有趣的是,在這種情況下,“熱身”運行實際上似乎並不適用。
當利用延遲執行時,LINQ通常會更快。 就像這里一樣
如您所懷疑; foreach
在您的代碼中完全枚舉集合。 Select
只是建立一個查詢來進行該枚舉。
然后,當您調用Sum
,它將枚舉先前生成的集合。 我們總共有2個foreach
枚舉,而Select
只有一個枚舉,因此它更快(提高了2倍!)。
還有其他例子; Take
,並且First
將盡早停止執行( foreach
可以盡早停止,但是大多數人不會這樣編碼)。
基本上,當您實際需要枚舉所執行的操作的整個集合時,或者當您想要枚舉(使用)LINQ查詢時,請使用foreach
。 在運行查詢和操作時使用LINQ,在這些查詢和操作中延遲執行將為您帶來性能優勢。 當該查詢返回一個集合,使用foreach
遍歷它 。
您正在比較蘋果和橙子,Linq並不像您的“ non-lambda”版本那樣使用List <>。 該列表並非免費提供。
您需要這樣寫:
public IEnumerable<NumberObject> GetNumbersWithoutLambda() {
IEnumerable<Int32> numberRange = Enumerable.Range(0, 10);
foreach (Int32 item in numberRange) {
yield return new NumberObject() { Number = item };
}
}
現在所需的時間相同。 是的,Linq也使用迭代器。
然而,這並不望其項背的unlinquified版本,它是快5倍:
static int sum; // Ensures summing doesn't get optimized away
private void runGetNumbers2(Int32 numberOfTimesToRun) {
for (int i = 0; i < numberOfTimesToRun; i++) {
foreach (var number in Enumerable.Range(0, 10)) {
sum += number;
}
}
}
使其更快另一三倍還通過降低Enumerable.Range:
for (int i = 0; i < numberOfTimesToRun; i++) {
for (int j = 0; j < 10; ++j) {
sum += j;
}
}
這表明迭代器使用的狀態機也不是免費的。 這里的基本前提是簡單的代碼是快速的。
區別在於,即使List實現IEnumerable,也必須在該方法可以返回之前完全填充它,而Linq方法只需要在返回之前構造表達式樹即可。
考慮並計時以下內容:
public IEnumerable<NumberObject> GetNumbersWithLambdaToList()
{
IEnumerable<Int32> numberRange = Enumerable.Range(0, 10);
IEnumerable<NumberObject> numbers = numberRange.
Select(number => new NumberObject() { Number = number });
return numbers.ToList();
}
public IEnumerable<NumberObject> GetNumbersWithYield()
{
IEnumerable<Int32> numberRange = Enumerable.Range(0,10);
foreach (Int32 item in numberRange)
{
yield return (new NumberObject() { Number = item });
}
}
在我的機器上:
GetNumbersWithoutLambda: 9631 milliseconds
GetNumbersWithLambda: 7285 milliseconds
GetNumbersWithLambdaToList: 12998 milliseconds
GetNumbersWithYield: 9236 milliseconds
不完全是。 取決於您對Linq所做的一切
如果僅使用foreach
來迭代和修改集合中的項目,而無需創建新集合。 那Linq一般都比較慢
但是大多數人傾向於按照其長邏輯來創建集合以進行迭代。 這樣分配內存並使其比使用Linq慢,因為Linq不會創建實際的集合,它只記得在foreach
時執行的迭代邏輯
如果沒有必要,過度使用Linq有時會減慢速度。 因為使用Linq創建委托對象並導致堆棧分配
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.