1

我已经用and循环测试了List<string>vsIEnumerable<string> 迭代,列表是否可能更快?forforeach

这些是我能找到的几个链接中的 2 个,它们公开声明性能更好地IEnumerable迭代List

链接 1 链接2

我的测试是从一个包含 URL 列表的文本文件中加载 10K 行。

我首先将其加载到 List ,然后将 List 复制到 IEnumerable

List<string> StrByLst = ...method to load records from the file .
IEnumerable StrsByIE =  StrByLst;

所以每个都有 10k 个项目类型<string>

在每个集合上循环 100 次,意味着 100K 次迭代,结果是

List<string>_ _IEnumerable<string>

这是可以预测的吗?

  • 更新

这是进行测试的代码

string WorkDirtPath = HostingEnvironment.ApplicationPhysicalPath;
    string fileName = "tst.txt";
    string fileToLoad = Path.Combine(WorkDirtPath, fileName);
    List<string> ListfromStream = new List<string>();
    ListfromStream =  PopulateListStrwithAnyFile(fileToLoad) ;
    IEnumerable<string> IEnumFromStream = ListfromStream ;

    string trslt = "";
    Stopwatch SwFr = new Stopwatch();
    Stopwatch SwFe = new Stopwatch();

    string resultFrLst = "",resultFrIEnumrable, resultFe = "", Container = "";

    SwFr.Start();

    for (int itr = 0; itr < 100; itr++)
    {
        for (int i = 0; i < ListfromStream.Count(); i++)
        {
            Container = ListfromStream.ElementAt(i);
        }
    //the stop() was here , i was doing changes , so my mistake.
    }

   SwFr.Stop();
   resultFrLst = SwFr.Elapsed.ToString();
   //forgot to do this reset though still it is faster (x56??)
   SwFr.Reset();
   SwFr.Start();
        for(int itr = 0; itr<100; itr++)
        {
            for (int i = 0; i < IEnumFromStream.Count(); i++)
            {
                Container = IEnumFromStream.ElementAt(i);
            }
        }
    SwFr.Stop();
    resultFrIEnumrable = SwFr.Elapsed.ToString();

更新...最终

将计数器移到 for 循环之外,

int counter = ..count对于 IEnumerable 和 List

然后按照@ScottChamberlain 的建议将 counter(int) 作为项目总数传递。重新检查所有东西都已到位,现在结果比 IEnumerable 快 5 %。因此得出结论,按场景使用 - 用例......根本没有性能差异......

4

3 回答 3

4

你做错了什么。

你得到的时间应该非常接近,因为你运行的是基本相同的代码。

IEnumerable 只是 List 实现的一个接口,因此当您在 IEnumerable 引用上调用某个方法时,它最终会调用 List 的相应方法。

IEnumerable 中没有实现代码——这就是接口——它们只指定一个类应该具有的功能,但没有说明它是如何实现的。

于 2012-12-16T07:22:06.460 回答
3

您的测试有一些问题,一个是循环IEnumFromStream.Count()内部for,每次它想要获取该值时,它必须枚举整个列表以获取计数,并且该值不会在循环之间缓存。将该调用移出for循环并将结果保存在 a 中int并将该值用于for循环,您将看到 IEnumerable 的时间更短。

与它类似的IEnumFromStream.ElementAt(i)行为也Count()必须遍历整个列表直到i(例如:第一次0,第二次0,1,第三次0,1,2,等等......)每次List可以直接跳转到它需要的索引。您应该使用IEnumerator返回的 fromGetEnumerator()而不是。

IEnumerable's 和forloop's 不能很好地混合。使用正确的工具来完成工作,要么调用GetEnumerator()并使用它,要么foreach循环使用它。


现在,我知道你们中的很多人可能会说“但它是一个接口,它只是映射调用,应该没有区别”,但有一个关键,IEnumerable<T> 没有Count()orElementAt()方法!. 这些方法是 LINQ 添加的扩展方法,并且 LINQ 类不知道底层集合是一个 List,所以它会做它知道底层对象可以做的事情,那就是每次调用该方法时迭代列表。


IEnumerable使用IEnumerator

using(var enu = IEnumFromStream.GetEnumerator())
{
    //You have to call "MoveNext()" once before getting "Current" the first time,
    //   this is done so you can have a nice clean while loop like this.
    while(enu.MoveNext())
    {
        Container = enu.Current;
    }
}

上面的代码基本上是一样的

foreach(var enu in IEnumFromStream)
{
    Container = enu;
}

要记住的重要一点是IEnumerable's 没有长度,实际上它们可以无限长。计算机科学的整个领域都在检测无限长的IEnumerable

于 2012-12-16T07:55:13.703 回答
1

根据您发布的代码,我认为问题出在您对Stopwatch课程的使用上。

您声明其中两个,SwFrand SwFe,但只使用前者。因此,最后一次调用将获得两组循环的SwFr.Elapsed总时间。for

如果您想以这种方式重用该对象,请立即调用SwFr.Reset()after resultFrLst = SwFr.Elapsed.ToString();

或者,您可以SwFe在运行第二个测试时使用。

于 2012-12-16T07:36:48.590 回答