为什么我的IEnumerable <string>使用yield返回较慢迭代然后List <string> </string> </string>

时间:2008-12-24 01:20:30

标签: c# ienumerable

我一直在用我编写的一些代码测试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);
        }

有趣的是,由于某种原因,我似乎能够循环并打印出比{MyyieldCollection更快的完整MyListCollection

结果:

  • MyYieldCollection - &gt; 2062
  • MyListCollection - &gt; 1847年

我真的看不出这个的原因,我错过了什么或这是正常的吗?

4 个答案:

答案 0 :(得分:4)

你是如何完成时间的?你在调试器里吗?在调试模式?看起来您正在使用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++) {...}

所以我不会重现你的数字......

答案 1 :(得分:1)

如果循环的一次迭代很昂贵并且您只需要迭代集合中的几个项目会发生什么?

有了收益,你只需支付所得的费用;)

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;
        }
    }
}

答案 2 :(得分:0)

我的猜测是JIT可以更好地优化返回列表的版本中的for循环。在返回IEnumerable的版本中,for循环中使用的行变量现在实际上是生成的类的成员,而不是仅对该方法本地的变量。

速度差异只有10%左右,所以除非这是性能关键代码,否则我不担心。

答案 3 :(得分:-1)

据我所知,“yield return”将继续循环,直到它运行我们要做的事情并且函数/属性退出,返回一个填充的IEnumarable。换句话说,不是为foreach循环中的每个项调用函数,而是在执行foreach循环内的任何内容之前调用它。

可能是返回的集合类型。也许List可以比IEnumerable的任何数据结构更快地迭代。