Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In .NET, using "foreach" to iterate an instance of IEnumerable<ValueType> will create a copy? So should I prefer to use "for" instead of "foreach"?

Tags:

In .NET, using "foreach" to iterate an instance of IEnumerable will create a copy? So should I prefer to use "for" instead of "foreach"?

I wrote some code to testify this:

struct ValueTypeWithOneField {     private Int64 field1; }  struct ValueTypeWithFiveField {     private Int64 field1;     private Int64 field2;     private Int64 field3;     private Int64 field4;     private Int64 field5; }  public class Program {     static void Main(string[] args)     {         Console.WriteLine("one field");         Test<ValueTypeWithOneField>();          Console.WriteLine("-----------");          Console.WriteLine("Five field");         Test<ValueTypeWithFiveField>();          Console.ReadLine();     }      static void Test<T>()     {         var test = new List<T>();         for (int i = 0; i < 5000000; i++)         {             test.Add(default(T));         }          Stopwatch sw = new Stopwatch();          for (int i = 0; i < 5; i++)         {             sw.Start();              foreach (var item in test)             {              }              sw.Stop();             Console.WriteLine("foreach " + sw.ElapsedMilliseconds);             sw.Restart();              for (int j = 0; j < test.Count; j++)             {                 T temp = test[j];             }              sw.Stop();             Console.WriteLine("for " + sw.ElapsedMilliseconds);             sw.Reset();         }     }} 

And this is the result that I got after I ran the code:

    one field     foreach 68     for 72     foreach 68     for 72     foreach 67     for 72     foreach 64     for 73     foreach 68     for 72     -----------     Five field     foreach 272     for 193     foreach 273     for 191     foreach 272     for 190     foreach 271     for 190     foreach 275     for 188 

As we can see in the result, "foreach" always takes more time than "for".

So should I prefer to use "for" instead of "foreach" when iterating through a generic collection of value type?

Note: thanks for the reminder, I edited the code and result. but still, foreach is running slower than for.

like image 503
Cui Pengfei 崔鹏飞 Avatar asked Apr 14 '11 13:04

Cui Pengfei 崔鹏飞


People also ask

When we should use for each loop instead of for loop?

Only use the for-each loop when you want to loop through all the values in an array or list. If you only want to loop through part of an array or list use a for loop instead. Also use a for loop instead of a for-each loop if you want to change any of the values in the array or list.

Which is better for or foreach in C#?

The difference between for and foreach in C# is that for loop is used as a general purpose control structure while foreach loop is specifically used for arrays and collections. In brief, both helps to execute code repeatedly but foreach loop is more specific to arrays and collections.

Which .NET interface allows to use Class A in foreach loop in such way foreach VAR A in A?

The IEnumerator interface provides iteration over a collection-type object in a class. The IEnumerable interface permits enumeration by using a foreach loop.

Why use foreach instead of for of?

foreach loop in C# C# foreach loop is used to iterate through items in collections (Lists, Arrays etc.). When you have a list of items, instead of using a for loop and iterate over the list using its index, you can directly access each element in the list using a foreach loop.


2 Answers

Your question is way, way too complex. Break it down.

Does using “foreach” to iterate a sequence of value types create a copy of the sequence?

No.

Does using "foreach" to iterate a sequence of value types create a copy of each value?

Yes.

Does using "for" to do an equivalent iteration of an indexed sequence of value types create a copy of each value?

Usually, yes. There are things you can do to avoid the copying if you know special things about the collection, like for instance that it is an array. But in the general case of indexed collections, indexing the sequence returns a copy of the value in the sequence, not a reference to a storage location containing the value.

Does doing anything to a value type make a copy of the value?

Just about. Value types are copied by value. That's why they're called value types. The only things that you do to value types that do not make a copy are calls to methods on the value type, and passing a value type variable using "out" or "ref". Value types are copied constantly; that's why value types are often slower than reference types.

Does using "foreach" or "for" to iterate a sequence of reference type copy the reference?

Yes. The value of an expression of reference type is a reference. That reference is copied whenever it is used.

So what's the difference between value types and reference types as far as their copying behaviour is concerned?

Value types are copied by value. Reference types copy the reference but not the thing being referred to. A 16-byte value type copies 16 bytes every time you use it. A 16 byte reference type copies the 4 (or 8) byte reference every time you use it.

Is the foreach loop slower than the for loop?

Often it is. The foreach loop is often doing more work, in that it is creating an enumerator and calling methods on the enumerator, instead of just incrementing an integer. Integer increments are extremely fast. Also don't forget that the enumerator in a foreach loop has to be disposed, and that can take time as well.

Should I use the for loop instead of the foreach loop because the for loop is sometimes a few microseconds faster?

No. That's dumb. You should make smart engineering decisions based on customer-focussed empirical data. The extra burden of a foreach loop is tiny. The customer will probably never notice. What you should do is:

  • Set performance goals based on customer input
  • Measure to see if you've met your goals
  • If you have not, find the slowest thing using a profiler
  • Fix it
  • Repeat until you've met your goals

Odds are extremely good that if you have a performance problem, changing a foreach loop to a for loop will make no difference whatsoever to your problem. Write the code the way it looks clear and understandable first.

like image 63
Eric Lippert Avatar answered Oct 21 '22 23:10

Eric Lippert


Your test is not accurate; in the foreach version, you're actually spinning up the enumerator and retrieving each value from the list (even though you aren't using it). In the for version, you aren't doing anything with the list at all, other than looking at its Count property. You're essentially testing the performance of an enumerator traversing a collection compared to incrementing an integer variable an equivalent number of times.

To create parity, you'd need to declare a temporary variable and assign it in each iteration of the for loop.

That being said, the answer to your question is yes. A copy of the value will be created with every assignment or return statement.

Performance

This pseudocode breakdown should explain why foreach is somewhat slower than using for in this particular instance:

foreach:

try {     var en = test.GetEnumerator(); //creates a ListEnumerator     T item;      while(en.MoveNext()) // MoveNext increments the current index and returns                          // true if the new index is valid, or false if it's                          // beyond the end of the list. If it returns true,                          // it retrieves the value at that index and holds it                           // in an instance variable     {         item = en.Current; // Current retrieves the value of the current instance                            // variable     } } finally { } 

for:

int index = -1; T item;  while(++index < test.Count) {     item = test[index]; } 

As you can see, there's simply less code in the for implementation, and foreach has a layer of abstraction (the enumerator) on top of the for. I wrote the for using a while loop to show the two versions in a similar representation.

With all that said...

You're talking about a trivial difference in execution time. Use the loop that makes the code clearer and smaller, and in this circumstance that looks like foreach.

like image 42
Adam Robinson Avatar answered Oct 22 '22 00:10

Adam Robinson