簡體   English   中英

“收益率回報”是否比“舊學校”回歸慢?

[英]Is 'yield return' slower than “old school” return?

我正在做一些關於收益率回歸性能的測試,我發現它比正常回報慢。

我測試了值變量(int,double等)和一些引用類型(字符串等)......並且兩種情況下的yield return都較慢。 為什么要用呢?

看看我的例子:

public class YieldReturnTeste
{
    private static IEnumerable<string> YieldReturnTest(int limite)
    {
        for (int i = 0; i < limite; i++)
        {
            yield return i.ToString();
        }
    }

    private static IEnumerable<string> NormalReturnTest(int limite)
    {
        List<string> listaInteiros = new List<string>();

        for (int i = 0; i < limite; i++)
        {
            listaInteiros.Add(i.ToString());
        }
        return listaInteiros;
    }

    public static void executaTeste()
    {
        Stopwatch stopWatch = new Stopwatch();

        stopWatch.Start();

        List<string> minhaListaYield = YieldReturnTest(2000000).ToList();

        stopWatch.Stop();

        TimeSpan ts = stopWatch.Elapsed;


        string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",

        ts.Hours, ts.Minutes, ts.Seconds,

        ts.Milliseconds / 10);

        Console.WriteLine("Yield return: {0}", elapsedTime);

        //****

        stopWatch = new Stopwatch();

        stopWatch.Start();

        List<string> minhaListaNormal = NormalReturnTest(2000000).ToList();

        stopWatch.Stop();

        ts = stopWatch.Elapsed;


        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",

        ts.Hours, ts.Minutes, ts.Seconds,

        ts.Milliseconds / 10);

        Console.WriteLine("Normal return: {0}", elapsedTime);
    }
}

考慮File.ReadAllLinesFile.ReadLines之間的區別。

ReadAllLines將所有行加載到內存中並返回一個string[] 如果文件很小,一切都很好。 如果文件大於適合內存的文件,則內存不足。

另一方面, ReadLines使用yield return return一次返回一行。 有了它,您可以閱讀任何大小的文件。 它不會將整個文件加載到內存中。

假設您要查找包含單詞“foo”的第一行,然后退出。 使用ReadAllLines ,即使第一行出現“foo”,您也必須將整個文件讀入內存。 使用ReadLines ,您只能閱讀一行。 哪一個會更快?

這不是唯一的原因。 考慮一個讀取文件並處理每一行的程序。 使用File.ReadAllLines ,您最終得到:

string[] lines = File.ReadAllLines(filename);
for (int i = 0; i < lines.Length; ++i)
{
    // process line
}

程序執行所花費的時間等於讀取文件所需的時間,加上處理行的時間。 想象一下,處理需要很長時間,以至於你想用多個線程來加速它。 所以你做的事情如下:

lines = File.ReadAllLines(filename);
Parallel.Foreach(...);

但閱讀是單線程的。 在主線程加載整個文件之前,您的多個線程無法啟動。

但是,使用ReadLines ,您可以執行以下操作:

Parallel.Foreach(File.ReadLines(filename), line => { ProcessLine(line); });

這會立即啟動多個線程,這些線程在讀取其他線路的同時進行處理。 因此,讀取時間與處理時間重疊,這意味着您的程序將更快地執行。

我使用文件展示我的示例,因為它更容易以這種方式演示概念,但對於內存中的集合也是如此。 使用yield return將使用更少的內存並且可能更快,特別是在調用只需要查看集合的一部分的方法時( Enumerable.AnyEnumerable.First等)。

首先,它是一個方便的功能。 二,它允許你做懶惰返回,這意味着它只在值被提取時進行評估。 這在像數據庫查詢這樣的東西中是非常寶貴的,或者只是一個你不想完全迭代的集合。 三,在某些情況下可能會更快。 四,有什么區別? 可能很小,所以微觀優化。

因為C#編譯器將迭代器塊( yield return )轉換為狀態機。 在這種情況下,狀態機非常昂貴。

你可以在這里閱讀更多內容: http//csharpindepth.com/articles/chapter6/iteratorblockimplementation.aspx

我使用yield return來給出算法的結果。 每個結果都基於以前的結果,但我並不需要所有結果。 我使用帶有yield return的foreach來檢查每個結果,如果我得到滿足我要求的結果,則打破foreach循環。

算法很復雜,所以我認為在每個收益率回報之間保存狀態需要一些體面的工作。

我注意到它比傳統回報慢了3%-5%,但我得到的改進不需要產生所有結果,遠遠大於性能損失。

.ToList()雖然是真正完成IEnumerable的延遲迭代所必需的, .ToList()阻礙了測量核心部分。

至少將列表初始化為已知大小非常重要:

const int listSize = 2000000; var tempList = new List(listSize);

...

列表tempList = YieldReturnTest(listSize).ToList();

備注:這兩個電話在我的機器上花了大約相同的時間..沒有區別(repl.it上的Mono 4)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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