Reputation: 2163
I've tested List<string>
vs IEnumerable<string>
iterations with for
and foreach
loops , is it possible that the List is much faster ?
these are 2 of few links I could find that are publicly stating that performance is better iterating IEnumerable
over List
.
my tests was loading 10K lines from a text file that holds a list of URLs.
I've first loaded it in to a List , then copied List to an IEnumerable
List<string> StrByLst = ...method to load records from the file .
IEnumerable StrsByIE = StrByLst;
so each has 10k items Type <string>
looping on each collection for 100 times , meaning 100K iterations, resulted with
List<string>
is faster by amazing 50 x than the IEnumerable<string>
is that predictable ?
this is the code that is doing the tests
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();
Update ... final
taking out the counter to outside of the for loops ,
int counter = ..count
for both IEnumerable & List
then passed counter(int) as a count of total items as suggested by @ScottChamberlain . re checked that every thing is in place, now the results are 5 % faster IEnumerable. so that concludes , use by scenario - use case... no performance difference at all ...
Upvotes: 1
Views: 236
Reputation: 127563
You have a few problems with your test, one is the IEnumFromStream.Count()
inside the for
loop, every time it want to get that value it must enumerate over the entire list to get the count and the value is not cached between loops. Move that call outside of the for
loop and save the result in a int
and use that value for the for
loop, you will see a shorter time for your IEnumerable.
Also the IEnumFromStream.ElementAt(i)
behaves similarly to Count()
it must iterate over the whole list up to i
(eg: first time it goes 0
, second time 0,1
, third 0,1,2
, and so on...) every time where List
can jump directly to the index it needs. You should be working with the IEnumerator
returned from GetEnumerator()
instead.
IEnumerable
's and for
loop's don't mix well. Use the correct tool for the job, either call GetEnumerator()
and work with that or use it in a foreach
loop.
Now, I know a lot of you may be saying "But it is a interface it will be just mapping the calls and it should make no difference", but there is a key thing, IEnumerable<T>
Does not have a Count()
or ElementAt()
method!. Those methods are extension methods added by LINQ, and the LINQ classes do not know the underlying collection is a List, so it does what it knows the underlying object can do, and that is iterating over the list every time the method is called.
IEnumerable
using 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;
}
}
The above code is basically the same thing as
foreach(var enu in IEnumFromStream)
{
Container = enu;
}
The important thing to remember is IEnumerable
's do not have a length, in fact they can be infinitely long. There is a whole field of computer science on detecting a infinitely long IEnumerable
Upvotes: 3
Reputation: 14938
Based on the code you posted I think the problem is with your use of the Stopwatch
class.
You declare two of these, SwFr
and SwFe
, but only use the former. Because of this, the last call to SwFr.Elapsed
will get the total amount of time across both sets of for
loops.
If you are wanting to reuse that object in this way, place a call to SwFr.Reset()
right after resultFrLst = SwFr.Elapsed.ToString();
.
Alternatively, you could use SwFe
when running the second test.
Upvotes: 1
Reputation: 93030
You are doing something wrong.
The times that you get should be very close to each other, because you are running essentially the same code.
IEnumerable is just an interface, which List implements, so when you call some method on the IEnumerable reference it ends up calling the corresponding method of List.
There is no code implemented in the IEnumerable - this is what interfaces are - they only specify what functionality a class should have, but say nothing about how it's implemented.
Upvotes: 4