简单列表<string> vs IEnumerable <string>性能问题</string> </string>

时间:2012-12-16 07:15:12

标签: c# performance collections

我测试了List<string> vs IEnumerable<string> 使用forforeach循环进行迭代,List可能更快吗?

这些是我能找到的少数链接中的两个,这些链接公开声明性能更好地迭代IEnumerable而不是List

Link1 Link2

我的测试是从包含网址列表的文本文件中加载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>

惊人的 50 x 更快

是可以预测的吗?

  • 更新

这是进行测试的代码

 
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循环之外,

IEnumerable&amp;的

int counter = ..count列表

然后传递counter(int)作为@ScottChamberlain建议的总项数。 重新检查每件事情是否到位,现在结果是IEnumerable快了5%。 总结,使用情景 - 用例...根本没有性能差异......

3 个答案:

答案 0 :(得分:4)

你做错了什么。

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

IEnumerable只是一个List实现的接口,所以当你在IEnumerable引用上调用一些方法时,它最终会调用List的相应方法。

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

答案 1 :(得分:3)

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

IEnumFromStream.ElementAt(i)的行为与Count()类似,它必须在整个列表中迭代到i(例如:第一次0,第二次{{1}每次0,1可以直接跳转到它需要的索引时,},第三个0,1,2等等...)您应该使用IEnumerator返回的GetEnumerator()代替。

ListIEnumerable循环不能很好地混合。使用正确的工具进行工作,请致电GetEnumerator()并使用该工具或在for循环中使用它。


现在,我知道很多人可能会说“但这是一个界面,它只是映射调用,它应该没有区别”,但有一个关键的事情,foreach 没有IEnumerable<T>Count()方法! 。这些方法是LINQ添加的扩展方法,LINQ类不知道底层集合是List,因此它知道底层对象可以做什么,并且每次调用该方法时都会遍历列表。 / p>


ElementAt()使用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; } 没有长度,实际上它们可以无限长。有a whole field计算机科学检测无限长的IEnumerable

答案 2 :(得分:1)

根据您发布的代码,我认为问题在于您使用Stopwatch类。

您声明其中两个,SwFrSwFe,但仅使用前者。因此,对SwFr.Elapsed的最后一次调用将获得两个 for个循环集合的总时间。

如果您想以这种方式重复使用该对象,请在SwFr.Reset()之后立即拨打resultFrLst = SwFr.Elapsed.ToString();

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