Search Unity

AnimationStates allocate piles of memory

Discussion in 'iOS and tvOS' started by MikaMobile, Apr 9, 2009.

  1. MikaMobile

    MikaMobile

    Joined:
    Jan 29, 2009
    Posts:
    845
    I just discovered something that I wish I'd known a long time ago...

    Referring to AnimationStates in an update loop, such as in the following code...

    Code (csharp):
    1.  
    2. function Update () {
    3.      if (animation["idle"].weight == 1) {
    4.          // do stuff
    5.      }
    6. }
    ... allocates massive amounts of memory each update. My first iPhone game ended up using this kind of logic quite a bit to determine animation behavior, when to play what clip, with what weight, etc. etc. Turns out it was causing the garbage collector to run overtime due to the fact that each time you use an animation state in this manner, you're burning memory. On the contrary...

    Code (csharp):
    1.  
    2. var idle : AnimationState;
    3. function Awake () {
    4.      idle = animation["idle"];
    5. }
    6.  
    7. function Update () {
    8.      if (idle.weight == 1) {
    9.           // do stuff
    10.      }
    11. }
    ...causes no runtime allocation. Just thought others might like to know.
     
  2. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    These little optimization hints are much appreciated!
     
  3. junkotron

    junkotron

    Joined:
    Feb 22, 2009
    Posts:
    158
    is the general rule to avoid strings in Updates as much as possible? i'm starting optimization in my game now and i think i have quite a few GameObject.Find("blah") throughout which i'm thinking probably isn't such a good idea.
     
  4. MikaMobile

    MikaMobile

    Joined:
    Jan 29, 2009
    Posts:
    845
    As a general rule, whenever you're referring to other objects or components via script on a frame-to-frame basis, its best to cache a reference to that object/component in a variable. It is both faster, and does not require repeated memory allocation. If you need to find another object by name with GameObject.Find(), try to do it only once (such as in an Awake () function) and store it in a variable for repeated use.

    If you're ever in doubt as to whether you're scripts are causing memory allocations every frame, build your app in xcode and choose Run > Start with Performance Tool > Activity Monitor from the pull down menus. If your app's memory usage is slowly climbing for no apparent reason, you know you have allocations occurring every frame. After about 4 mb accumulate, the garbage collector will run and you will experience a small performance hit... which is really annoying if you're making an action game! (FYI, a lot of people observe this behavior and think its memory leaks, but this is not the case - just garbage accumulating and being discarded.)

    My first iphone game ended up allocating a fair amount of tiny junk all the time and as such wil experience these performance hits for a couple seconds once every 2-3 minutes during play. I hope to write cleaner code and avoid it altogether in my current project - ideally garbage collection will almost never need to run in the midst of gameplay.
     
  5. junkotron

    junkotron

    Joined:
    Feb 22, 2009
    Posts:
    158
    Sounds like a good tip. I tried running the Activity Monitor but I keep getting a "Target terminated too early to collect data". App runs just fine though.
     
  6. ReJ

    ReJ

    Unity Technologies

    Joined:
    Nov 1, 2008
    Posts:
    378
    Another way to find it out: set ENABLE_INTERNAL_PROFILER to 1 (instead of 0) in AppController.mm. Run you game on iPhone and check XCode console output. If you see "used heap" in "mono-memory" section increasing with each frame - you have temporary allocations in your scripts.
     
  7. junkotron

    junkotron

    Joined:
    Feb 22, 2009
    Posts:
    158
    I tried doing this but as the Instruments app opens it suddenly will say "Target terminated too early to collect data" and doesn't read anything.

    Any ideas what's going on here?
     
  8. dawvee

    dawvee

    Joined:
    Nov 12, 2008
    Posts:
    276
    Thanks for the great tip, PirateNinja! I had noticed this behaviour myself (gradually climbing memory usage) but hadn't quite copped that a lot of it was likely allocations from just accessing various properties in Update.
     
  9. MikeB

    MikeB

    Joined:
    Mar 19, 2009
    Posts:
    7
    Code:

    var idle : AnimationState;
    function Awake () {
    idle = animation["idle"];
    }

    function Update () {
    if (idle.weight == 1) {
    // do stuff
    }
    }

    Just wondering. Would this cache the same way (and be as beneficial) if I were to assign the "idle" animation to the "idle" variable in the Unity interface instead of an Awake function? Thanks.
     
  10. MikaMobile

    MikaMobile

    Joined:
    Jan 29, 2009
    Posts:
    845
    That code you have there is exactly what you should do. Assigning through the inspector would achieve the same result, but I've personally gotten in the habit of assigning it all via script because I'm doing a lot of animation setup in my awake function anyway (setting layers, wrap modes, speeds and such).
     
  11. MikeB

    MikeB

    Joined:
    Mar 19, 2009
    Posts:
    7
    Thanks for the fast reply:) Assigning through script definitely sounds like the way to go. I'll know for next time. Thanks again.
     
  12. psychicparrot

    psychicparrot

    Joined:
    Dec 10, 2007
    Posts:
    884
    Sorry to bring this up again, but what happens in the case of:

    animation.CrossFade("Run");

    Is this still getting treated as a string and allocating memory all the time?

    Thanks!
     
  13. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    "Run" is a string and as such is allocated just to be dropped again.
    If you don't want that to happen, cache it in a string array and use an array entry instead
     
  14. psychicparrot

    psychicparrot

    Joined:
    Dec 10, 2007
    Posts:
    884
    Thanks, man. Makes total sense!

    I just make a string array and hopefully all that nasty memory allocation nonsense should ease off! :)
     
  15. MikaMobile

    MikaMobile

    Joined:
    Jan 29, 2009
    Posts:
    845
    Animation.Play("blah") or Animation.CrossFade("blah") do not allocate memory in my tests. I use statements like these constantly (multiple times per frame) and my mono heap does not grow each frame.
     
  16. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Thats clear pirate. it will only rise the heap once and after that use the pooled memory. but its still an object creation and destruction again and again.
     
  17. MikaMobile

    MikaMobile

    Joined:
    Jan 29, 2009
    Posts:
    845
    So then... pulling the names of animation clips from a pre-defined array won't save any memory, but it might require less cpu?

    Creating/destroying a string over and over again in this manner must have a very tiny effect, I do it dozens of times per second and have experienced no adverse effects.
     
  18. psychicparrot

    psychicparrot

    Joined:
    Dec 10, 2007
    Posts:
    884
    So I'm seeing A LOT less movement in the heap since I removed all my strings of doom, though something is still going on for the heap to keep growing albeit slower.

    Thanks for the heads up(s) ... very good to know!
     
  19. spacefrog

    spacefrog

    Joined:
    Jun 14, 2009
    Posts:
    734
    After browsing through this thread, i'm very happy that in my current Game, i recently changed all of the code to cache all AnimationState's to variables in Awake() .
    As it seems, this saves me from having nightmarish support-experiences after the game is out in the wild....
     
  20. jtbentley

    jtbentley

    Joined:
    Jun 30, 2009
    Posts:
    1,397
    I find this interesting.. I'm assuming its string related as to why that was blowing out...

    But what interests me is that I've never checked for whether an animation is playing, I generally make co-routines that wait a specified amount of time (the length of an animation) so I always know what has been played and what hasn't, etc.

    I know that sounds like far more work than required, but it means I'm not putting an if statement into an Update loop (I'm a code Nazi, i hate anything happening anywhere that doesn't need to).

    One of these days, I really should download one of the Unity example projects and see how 'normal people' do these things... I kinda just fumble my way through until something works :p
     
  21. zannghast

    zannghast

    Joined:
    Aug 17, 2010
    Posts:
    80
    I'm about a year late on this really great tip, but, hey, never too late to try anything XD

    Great tip btw!

    oh and @JTBentley I actually find the coroutine way a much simpler and faster way of waiting for animations to stop.