4

I'm refactoring my app to make it faster. I was looking for tips on doing so, and found this statement:

"ForEach can simplify the code in a For loop but it is a heavy object and is slower than a loop written using For."

Is that true? If it was true when it was written, is it still true today, or has foreach itself been refactored to improve performance?

I have the same question about this tip from the same source:

"Where possible use arrays instead of collections. Arrays are normally more efficient especially for value types. Also, initialize collections to their required size when possible."

UPDATE

I was looking for performance tips because I had a database operation that was taking several seconds.

I have found that the "using" statement is a time hog.

I completely solved my performance problem by reversing the for loop and the "using" (of course, refactoring was necessary for this to work).

The slower-than-molasses code was:

for (int i = 1; i <= googlePlex; i++) {
    . . .
    using (OracleCommand ocmd = new OracleCommand(insert, oc)) {
    . . .
    InsertRecord();
    . . .

The faster-than-a-speeding-bullet code is:

using (OracleCommand ocmd = new OracleCommand(insert, oc)) {
    for (int i = 1; i <= googlePlex; i++) {
        . . .
        InsertRecord();
        . . .
B. Clay Shannon
  • 1,055
  • 124
  • 399
  • 759
  • 3
    Who is your source? These are micro-optimizations - in most apps design-level things like lock contention and network or file I/O are far more useful targets to optimize than stuff like this. Use a profiler and fix what's broken, not what might be. – Steve Townsend May 21 '12 at 16:41
  • 2
    `ForEach` is a "heavy object"? Didn't know that. Please tell me more. – Tudor May 21 '12 at 16:42
  • 3
    Sounds like an utterly broken source to me. Please post more details so we know to avoid it. – Jon Skeet May 21 '12 at 16:44
  • @jon - http://www.csharphelp.com/2010/02/c-best-practices-to-write-high-performance-code/. It's a bit of a mixed bag, to be sure. – Steve Townsend May 21 '12 at 16:46
  • Yep, that's the one (the csharphelp link). – B. Clay Shannon May 21 '12 at 16:52
  • 5
    _I'm refactoring my app to make it faster_ - Then first find the slow parts. Don't optimize at random. – Henk Holterman May 21 '12 at 16:55
  • That's the problem with these general tips on performance, they are general. Guy's is walking talking proof that a little knowledge is dangerous. – Tony Hopkinson May 21 '12 at 16:57
  • What's inside the loop is way slower than the looping itself. Find a way to remove the loop entirely, or speed up the loop contents first. – Bryan Boettcher May 21 '12 at 17:02
  • It seems you don't have problems with foreach, or even using, but with managing resources. The using statement only causes the implicit cleanup of the resource created in new OracleCommand when exiting the using context. As you found out, it's better here to create the resource once outside the loop, re-use it inside, and then cleanup when the loop is exited. – ILoveFortran May 28 '12 at 14:18

4 Answers4

9

Short answer:

Code that is hard to read eventually results in software that behaves and performs poorly.

Long answer:

There was a culture of micro-optimization suggestions in early .NET. Partly it was because a few Microsoft's internal tools (such as FxCop) had gained popularity among general public. Partly it was because C# had and has aspirations to be a successor to assembly, C, and C++ regarding the unhindered access to raw hardware performance in the few hottest code paths of a performance critical application. This does require more knowledge and discipline than a typical application, of course. The consequences of performance related decisions in framework code and in app code are also quite different.

The net impact of this on C# coding culture has been positive, of course; but it would be ridiculous to stop using foreach or is or "" just in order to save a couple CIL instructions that your recent jitter could probably optimize away completely if it wanted to.

There are probably very many loops in your app and probably at most one of them might be a current performance bottleneck. "Optimizing" a non-bottleck for perfomance at the expense of readability is a very bad deal.

Jirka Hanika
  • 12,574
  • 3
  • 39
  • 68
6

It's true in many cases that foreach is slower than an equivalent for. It's also true that

for (int i = 0; i < myCollection.Length; i++) // Compiler must re-evaluate getter because value may have changed

is slower than

int max = myCollection.Length;
for (int i = 0; i < max; i++) 

But that probably will not matter at all. For a very detailed discussion see Performance difference for control structures 'for' and 'foreach' in C#

Have you done any profiling to determine the hot spots of your application? I would be astonished if the loop management overhead is where you should be focusing your attention.

Community
  • 1
  • 1
Eric J.
  • 139,555
  • 58
  • 313
  • 529
  • 1
    How can you classify a statement that calls `ForEach` a "heavy object" as being "true in many cases"? – Tudor May 21 '12 at 16:43
  • I did not. I said that `foreach` is slower than an equivalent `for` in most cases (though if you happened to read my first revision... I modified my response a few seconds later). – Eric J. May 21 '12 at 16:45
  • `ForEach` (which he mentioned in the question) and `foreach` are different things. – Tudor May 21 '12 at 16:46
  • @Tudor foreach requires keeping an iterator object in memory, and requires a function call through the iterator for each object, for does not. Other than that I cant imagine there being any difference in speed, since in the vast majority of cases the iterator is just going to be storing the index anyway. – Chris Shain May 21 '12 at 16:46
  • He quoted a source that also capitalized For. It's not at all clear that he's talking TPL or Linq, so I assumed the casing was in error. Maybe I'm wrong. – Eric J. May 21 '12 at 16:47
  • @Chris Shain: Any way you take it, `foreach` is a looping construct while `ForEach` is a method, neither of them is an object. – Tudor May 21 '12 at 16:48
  • @Tudor - I doubt the poster of those perf tips even knows what `ForEach` is. I think the conflation with `foreach` here is probably accurate. The tips also talk about `For` loops after all. – Steve Townsend May 21 '12 at 16:48
  • @Tudor clearly, but I'm working on the assumption that the OP is not nitpicking over case and means the statements in each case, not the methods. – Chris Shain May 21 '12 at 16:48
  • @Tudor: I just weighed the 8 foreach loops in my code, and they weigh exactly 16 tons each. Whether that is "heavy" is a matter for discussion, I guess. – B. Clay Shannon May 21 '12 at 16:57
4

You should try profiling your code with Red Gate ANTS or something of that ilk - you will be surprised.

I found that in an application I was writing it was the parameter sniffing in SQL that took up 25% of the processing time. After writing a command cache which sniffed the params at the start of the application, there was a big speed boost.

Unless you are doing a large amount of nested for loops, I don't think you will see much of a performance benefit from changing your loops. I can't imagine anything but a real time application such as a game or a large number crunching or scientific application would need that kind of optimisation.

Charleh
  • 13,285
  • 3
  • 34
  • 54
  • 1
    The performance analysis built in to VS2010 is surprisingly good. I agree that ANTS is still the gold standard, but it's not cheap either. – Eric J. May 21 '12 at 16:49
  • The free trial is cheap (£0.00) for a little while :( – Charleh May 21 '12 at 16:51
  • What performance analysis is built into VS2010? I'm using the Professional Edition, and see no such thing in the Tools menu (or Project, or Refactor, or anywhere else). – B. Clay Shannon May 21 '12 at 17:04
  • I wasn't aware of any but I too am using professional which quite strangely is one of the lowest versions, with premium and ultimate being more featureful, so maybe it's in a more expensive edition – Charleh May 21 '12 at 18:47
2

Yes. The classic for is a bit faster than a foreach as the iteration is index based instead of access the element of the collection thought an enumerator

       static void Main()
    {
        const int m = 100000000;
        //just to create an array
        int[] array = new int[100000000];
        for (int x = 0; x < array.Length; x++) {
            array[x] = x;
        }


        var s1 = Stopwatch.StartNew();           
        var upperBound = array.Length;
        for (int i = 0; i < upperBound; i++)
        {

        }
        s1.Stop();
        GC.Collect();
        var s2 = Stopwatch.StartNew();
        foreach (var item in array) { 

        }
        s2.Stop();
        Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds *
            1000000) / m).ToString("0.00 ns"));
        Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds *
            1000000) / m).ToString("0.00 ns"));
        Console.Read();

        //2.49 ns
        //4.68 ns

        // In Release Mode

        //0.39 ns
        //1.05 ns


}
Massimiliano Peluso
  • 24,915
  • 6
  • 54
  • 67
  • 2
    Note that foreach doesn't *have* to use IEnumerable. It's perfectly capable of working via patterns without any interfaces being implemented. – Jon Skeet May 21 '12 at 16:49
  • Could you elaborate on that Jon? I thought that C# required the iterated object to implement IEnumerable. – Eric J. May 21 '12 at 16:51
  • Here - [Does Class need to implement IEnumerable to use Foreach](http://stackoverflow.com/questions/127233/does-class-need-to-implement-ienumerable-to-use-foreach) – Alexei Levenkov May 21 '12 at 16:54
  • Thanks for the reference Alexei! – Eric J. May 21 '12 at 16:57
  • Try this again with a LinkedList :) – Bryan Boettcher May 21 '12 at 17:03
  • I have edited my answer. I meant that the foreach still need an enumerator to get the next element against the classic for which access the collection thought the index. The classic for is "always" faster than a foreach – Massimiliano Peluso May 21 '12 at 17:04
  • @MassimilianoPeluso I wouldn't say `always`. It's not that hard to come up with a contrived example that makes it slower. For example, if the `Count` or `Length` method of a collection is an extraordinarily expensive operation then a classic `for` would be very inefficient. Additionally if using something like a LinkedList where access by index is expensive it is also not a good idea. – Servy May 21 '12 at 17:21
  • @Servy: True, but if that's the case, assign .Count or .Length to a variable before beginning iteration. – Eric J. May 21 '12 at 17:26
  • @servy a good developer should assign the value of .count or length to a variabile before starting the iteration(as eric said). Always is in between apostrophe just because i think that in softwre development use the word "always" does not make lots of sense – Massimiliano Peluso May 21 '12 at 17:39
  • @EricJ. I wouldn't consider that the 'classic' for loop. It's a simple modification, but it's a modification nonetheless. You're also assuming that it's constant even though it's expensive. It's result is not always constant (and sometimes it's important for the sake of the `for` loop to use the current count, not the cached count. @Massimiliano Peluso - I took the quotes to be there for emphasis, not to say that you actually mean "usually". – Servy May 21 '12 at 18:02