Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Is "foreach" still bad for performance?

Discussion in 'Scripting' started by pintianz, Oct 4, 2016.

  1. pintianz

    pintianz

    Joined:
    Mar 1, 2015
    Posts:
    25
    At Title suggest. I'm using 5.3.2f1 atm.

    Also what should I look out for/avoid to optimize and increase performance in c#?

    Thanks all!
     
  2. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,002
    If you're having performance problems and the profiler says it's the script with the foreach, then yes.
     
  3. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    Well, regardless of what the profiler says, it's probably not a good idea to throw away valuable CPU time due to a bug in the foreach implementation. I believe it's fixed in Unity 5.5 with the compiler upgrade, though I'm unsure if this will end up in earlier versions or not.

    To avoid it, don't use the foreach loop. There's never an actual need to as it's just syntactic sugar. For loops are the obvious replacement if applicable. Otherwise, you can use this approach to get the same functionality while avoiding the bug:

    Code (CSharp):
    1. var enumerator = collection.GetEnumerator();
    2.  
    3. while (enumerator.MoveNext())
    4. {
    5.      var element = enumerator.Current;
    6.      // Loop body.
    7. }
     
    Ylih, roojerry, Kalladystine and 2 others like this.
  4. Trexug

    Trexug

    Joined:
    Dec 2, 2013
    Posts:
    88
    @JasonBricco
    Is there really a bug in the for-each implementation? I though that the problem was simply that all collection types returned an object when GetEnumerator was called, causing allocation and thereby garbage. The change in 5.5, from my understanding, is that several collections will now return a struct and avoid allocation (heap allocation that is). Your code does not solve that problem, but perhaps there are more problems with for-each than I am aware of? (apart from the weird handling of clojure, which isn't really about performance)

    EDIT: Actually, it seems like you are right. Calling GetEnumerator on a lot of collection-types does provide a value-type enumerator - the problem is with the implementation of for-each, which will access the enumerator through the IEnumerable<T> or IEnumerable interface, causing the return value to be boxed.
     
    Last edited: Oct 5, 2016
  5. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Worth noting that using foreach on System.Array types is fine. Also - the issue isn't that it's "bad for performance" it's that it creates extra garbage and the GC is what can tank your framerate.
     
  6. Dave-Carlile

    Dave-Carlile

    Joined:
    Sep 16, 2012
    Posts:
    967
    Yes. This is a bug in the Mono implementation currently used by Unity.
     
  7. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Yep. For example foreach on List<T> should be fine. One place the real conundrum kicks in though is with a Dictionary<TKey, TValue> because it doesn't support indexing so it's necessary to use a foreach to enumerate over the values. You can use the GetEnumerator method as stated above and when calling it directly, at least on a Dictionary, will return a typed Enumerator for you and shouldn't be subject to boxing.

    https://msdn.microsoft.com/en-us/library/bb346997(v=vs.110).aspx

    It should be noted that the "foreach bug" doesn't exist on all collection types. I specifically mention Dictionary because it does exist there and will allocate (I believe something like 16 bytes) every time you execute the foreach loop. This probably would still hold true every time you call GetEnumerator as well, so you'd probably want to cache that enumerator as long as the contents of the dictionary aren't being changed. It's a tricky issue.
     
  8. SubZeroGaming

    SubZeroGaming

    Joined:
    Mar 4, 2013
    Posts:
    1,008
    Linq + Lambda = Strippers & Candy = Performance Awesomeness
     
  9. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Not always the case.
     
    travis_cossairt and KelsoMRK like this.
  10. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Pretty sure this isn't the case unless it falls to the backing array's enumerator. From my understanding, the reason it works for regular arrays is because the compiler turns the foreach into a straight for loop.
     
  11. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    That could be the case. I remember seeing comments previously saying that foreach on List<T> didn't allocate, but maybe it does now or maybe that comment was wrong. It was some time ago here in the forums.
     
    KelsoMRK likes this.
  12. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    This depends very much on what you're doing in that lambda expression. It's very easy to create a garbage monster.
     
    Dustin-Horne likes this.
  13. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    And internally Linq is going to do the same for / foreach logic, it just wraps it in a pretty easy to use method which may build out an expression or whole expression tree as well as boxing and allocating. Linq is never, or at least in no case that I can think of, going to produce code that performs better than anything you can write yourself.
     
    MV10 likes this.
  14. SubZeroGaming

    SubZeroGaming

    Joined:
    Mar 4, 2013
    Posts:
    1,008
    Rights, it's what the compiler converts it to anyways. Minus well save it some time. lol
     
  15. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Not exactly... It's still a separate method call that does the same for / foreach inside of it and there is a bit of extra overhead. So it doesn't get completely abstracted away by the compiler. :)
     
  16. SubZeroGaming

    SubZeroGaming

    Joined:
    Mar 4, 2013
    Posts:
    1,008
    I picture myself under you with my mouth open ready to accept your knowledge. ;)
     
  17. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Why do I feel the need to shower after reading that? :)
     
  18. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Hell, I wasn't even in the conversation and I'm going to go get a shot of penicillin just in case.
     
    B1QQ, Novack and Dustin-Horne like this.
  19. CulzeanHexWar

    CulzeanHexWar

    Joined:
    Sep 18, 2015
    Posts:
    7
    I just profiled three different ways to iterate over a dictionary. A standard foreach, GetEnumerator method and ElementAt method. I tested with a dictionary of gameobjects with int keys, (heh not smart use of the data structure). With 1000 and 10000 entries.
    This was running under Unity 5.5 and I could find no difference in gc allocations (6.6kb for 10000) or time taken between GetEnumerator or the foreach loop.

    ElementAt method was by far the worse, as ElementAt is 0(n). It also seemed to hit the gc with 400Kb allocations.
    In latest version foreach is by far the fastest and tidiest method to use. Hope the details help.
     
    mirage3d, leni8ec, Emanx140 and 14 others like this.