簡體   English   中英

為什么我的IEnumerable <String> 使用yield return較慢地迭代然后列出 <String>

[英]Why is my IEnumerable<String> using yield return slower to iterate then List<String>

我已經用我編寫的一些代碼測試了yield return語句。 我有兩種方法:

public static IEnumerable<String> MyYieldCollection {
        get 
        {
            wrapper.RunCommand("Fetch First From Water_Mains");
            for (int row = 0; row < tabinfo.GetNumberOfRows() ; row++) //GetNumberOfRows
                                                                      //will return 1000+ most of the time.
            {
                yield return wrapper.Evaluate("Water_Mains.col1");
                wrapper.RunCommand("Fetch Next From Water_Mains");
             }
        }
    }

public static List<String> MyListCollection
    {
        get
        {
            List<String> innerlist = new List<String>();

            wrapper.RunCommand("Fetch First From Water_Mains");
            for (int row = 0; row < tabinfo.GetNumberOfRows(); row++)
            {
                innerlist.Add(wrapper.Evaluate("Water_Mains.col1"));
                wrapper.RunCommand("Fetch Next From Water_Mains");
            }
            return innerlist;
        }
    }

然后我對每個集合使用一個foreach循環:

        foreach (var item in MyYieldCollection) //Same thing for MyListCollection.
        {
            Console.WriteLine(item);
        }

有趣的是,由於某種原因,我似乎可以循環並比MyListCollection更快地打印出完整的MyListCollection。

結果:

  • MyYieldCollection-> 2062
  • MyListCollection-> 1847年

我真的看不出有什么原因,我是否缺少某些東西,或者這是正常現象嗎?

您如何安排時間? 您在調試器中嗎? 在調試模式下? 看起來您正在使用DataTable ,因此我將您的代碼用作測試裝備的模板(每次創建1000行),並在命令行的發布模式下使用了如下所述的工具; 結果如下(括號中的數字是檢查它們是否做相同的工作):

Yield: 2000 (5000000)
List: 2100 (5000000)

測試線束:

static  void Main()
{
    GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
    int count1 = 0;
    var watch1 = Stopwatch.StartNew();        
    for(int i = 0 ; i < 5000 ; i++) {
        foreach (var row in MyYieldCollection)
        {
            count1++;
        }
    }
    watch1.Stop();

    GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
    int count2 = 0;
    var watch2 = Stopwatch.StartNew();
    for (int i = 0; i < 5000; i++)
    {
        foreach (var row in MyListCollection)
        {
            count2++;
        }
    }
    watch1.Stop();

    Console.WriteLine("Yield: {0} ({1})", watch1.ElapsedMilliseconds, count1);
    Console.WriteLine("List: {0} ({1})", watch2.ElapsedMilliseconds, count2);
}

(請注意,您通常不應該使用GC.Collect ,但是它可以用於平整性能測試的字段)

為了避免重復,我所做的唯一更改是對for循環:

int rows = tabinfo.Rows.Count;
for (int row = 0; row < rows; row++) {...}

所以我不會復制您的電話號碼...

如果循環的一次迭代很昂貴,而您只需要迭代集合中的幾個項目,會發生什么?

有了收益,您只需要為獲得的收益付費;)

public IEnumerable<int> YieldInts()
{
    for (int i = 0; i < 1000; i++)
    {
        Thread.Sleep(1000) // or do some other work
        yield return i;
    }
}

public void Main()
{
    foreach(int i in YieldInts())
    {
        Console.WriteLine(i);
        if(i == 42)
        {
            break;
        }
    }
}

我的猜測是,JIT可以更好地優化返回列表的版本中的for循環。 在返回IEnumerable的版本中,在for循環中使用的行變量現在實際上是所生成類的成員,而不是僅是方法本地的變量。

速度差異只有10%左右,因此除非這是性能關鍵代碼,否則我不會擔心。

據我了解,“收益回報”將一直循環運行,直到它運行我們要做的事情並且功能/屬性退出,並返回填充的IEnumarable。 換句話說,不是在foreach循環中為每個項目調用該函數,而是在執行一次foreach循環內的任何操作之前調用該函數一次。

可以根據返回的集合的類型。 列表的迭代速度可能比IEnumerable的任何數據結構都要快。

暫無
暫無

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

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