“Try to avoid foreach/for loops”–Over my Dead Body! | Visual Studio Feeds
Posted by jpluimers on 2011/03/16
Zack Owens wrote a nice article comparing various loop constructs.
Summary:
If the semantics are the same, it does not matter if you use foreach, for, while or do while: they all have equal speed.
So: choose the loop construct that best fits the problem you are trying to solve.
–jeroen
via: “Try to avoid foreach/for loops”–Over my Dead Body! | Visual Studio Feeds.
Alex said
A related interesting article: More fun with Enumerators.
Michael Thuma said
Iterator related constructs pay, because you can very simple exchange the storages implementation a lot easier. For an array only it fairly makes sense …
I also don’t use for each… very often.
Jolyon Smith said
NO! Zack goes to great lengths to mock the creator of some tests that do something worthwhile – compare the cost of using an iterator vs the cost of writing loops more “long-hand”.
Whilst there are SOME valid points about problems in the test strategy, Zack has missed the point of the tests themselves, because all he then does is use an iterator in 3 different ways. It is not at all surprising that in each case he gets very similar results.
FOREACH loops are bound to be slower than “long-hand” loops – it is inevitable. A FOREACH calls into existence and into play a great deal of infrastructure code to achieve a trivial piece of procedural behaviour (for many/most such loops) and hide it behind some not much more efficient (in terms of keyboard activity savings) syntactic sugar.
Neither Zack nor the post he is responding to make credible points. I am tempted to do my own tests and post my findings. :)
And all of this we should remember is being discussed in the context of .NET where the managed runtime plays a part in all such tests that is not relevant in the self-managed (it’s not UNmanaged – *I* do the management thank you very much! LOL ) Win32 world.
jpluimers said
I’m looking forward to your test results.
Though foreach can incur some overhead, it highly depends on how/where you use it.
Furthermore, loop overhead usually is a fraction of the loop body.
What many people bashing foreach seem to forget is that:
– you should strive to write maintainable code
– you should not prematurely optimize code
Usually foreach wins on maintainability.
–jeroen
Jolyon Smith said
Assuming the thing you are looping over supports it. if not, then you either have to provide it or accept a mix of loop constructs through force majeur, not choice and least of all fitness for purpose.
Consistency has long been recognised as significant factor in maintainability of code.
I think it’s true to say that anywhere “foreach” can be used, a “for” can be used instead. The reverse is certainly not (always) true.
Jeff Lefebvre said
Also, foreach was designed with generics in mind. How can you have an iterator when you don’t know what datatype it is?
John said
“foreach” isn’t such a win in terms of maintainability, essentially because you have no control over the order of the evaluation of the items, and that order can be changed independently from the loop itself.
We’ve bumped on issues related to loops using “foreach” when the iterator logic changed, various loops broke because the implementor implicitly assumed a direction to the evaluation.
In practice, it is a very rare case where the loop steps are truly independent, and not order-dependent.
jpluimers said
It is very funny to read “in practice, it is a very rare case” hinting that would hold for each and every program and programmer.
My experience is that for a lot of software I have written, that the order in which things are processed often does not matter at all.
Sometimes I even design for that: it makes it so much easier to run things in parallel (oh did I tell you: I love 8+ core systems ).
The foreach idiom abstracts you away from the loop index, but – when it makes sense – still allows the compiler to perform (sometimes really smart) optimizations both in managed and native environments.
Having an index can even work against you (for instance when using linked lists).
When you need the index, or the loop order, then you can (and often should) use another construct than foreach.
The cool thing is that the foreach idiom works equally well in both managed and native environments.
–jeroen
Mason Wheeler said
Then you were doing it wrong. The point of foreach is that all that matters is hitting each member once. If order is important then yes, you should be using an indexed loop instead.
John said
“that the order in which things are processed often does not matter at all”
That’s not “often”, but very rare in my experience, and chances are it’s the same in your code but you have not realized it. Try reversing the order of your most commonly used enumerator and see what happens before assuming you are order independent.
Processing instructions or data, creating objects, filling up data-structures or visual components, releasing objects, searches, etc. all those tasks are typically order-dependant. Apart from summing up numbers, I don’t see many cases where ordering doesn’t matter.
“Having an index can even work against you (for instance when using linked lists”
Linked list is an area where foreach is problematic too, if the enumerator was initially built first-to-last, and is later changed to last-to-first, code can break if the linked items are not immutable (both their values and their ordering), or if you relied implicitly on the ordering.
In practice, foreach is safe only when the collections and the values are immutable during the loop, and the processing you do is order-independent. That quite restrictive.
jpluimers said
I have seen a trend to immutable data structures over the last decade or so.
Most of my loop based logic (apart from UI stuff) is very much independent of the order executed. It makes life so much easier.
And if I do need a certain order, I don’t use foreach, but another loop construct.
–jeroen
CR said
“A FOREACH calls into existence and into play a great deal of infrastructure code”
Barely, if the enumerator is implemented as a record and the methods inlined (talking Delphi now). In fact, even if implemented using a class, a for/in loop doesn’t have to be less efficient – just set a flag to disable removals or additions when the enumerator is created (resetting it when MoveNext returns false and/or the enumerator is destroyed), and there won’t be any range checking required on each enumeration, in contrast to the ordinary for/to loop case.