簡體   English   中英

當Linq比Foreach快時,是否真的是這種情況?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM