Search Unity

Garbage Collection, Allocations, and Third Party Assets in the Asset Store

Discussion in 'General Discussion' started by Games-Foundry, Jun 21, 2012.

  1. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    Yeah, any future tests I run I'll do with normal and deep profiling to confirm conclusions aren't being skewed.
     
  2. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,368
    I see yeah, lots of stuff running simultaneously. The nature of Navmeshes (being baked offline) doesn't allow you to do real-time moddifications on the terrain. Arong Granberg A* is fine when you dont go over certain amount of nodes (don't exactly know the numbers). But the fastest thing that you can do with is waypoint navigation.
    Why don't you try waypoints? You can automatically replace each waypoint over your terrain by raycasting (in case you do some moddifications on your terrain at runtime) and then compute the links when done. ;)
    Looking at your videos/shots, you have a lots of characters (which means lots of CharactersControllers), you must find a way to create your own character controllers (kinematic rigid body, freeze some of their axis rotations, do some front/floor raycasting check to detect steps once per each X frames).
    Itween is fast, never had any problem with (even if I'm not using it right now). Megafiers is mega heavy (based on their vertex manipulations which is done on the CPU) but i've never worked with, neither know how is was build so i cannot suggest anything on it. VLight is also very heavy. If you want better volumetric effects that only uses your current camera (for depth testing), you should try this: http://u3d.as/content/stu-assets/volumetric-light-beam-kit
    Demo video:

    Btw, this kit allow you to have multiple volume lights without breaking your performance (aka adding extra buffers).
     
    Last edited: Jun 22, 2012
  3. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    @tatoforever Waypoints are fixed navigation paths, whereas our characters are controlled by the player and can go anywhere they are commanded when not in automatic mode ( where they go about their occupational tasks ). We also make use of penalties to define preferred highways so our characters take terrain painted paths instead of simply heading cross-country.

    Our level map is large at 2000x2000, with 1million pathfinding nodes. We need the granularity to ensure complex environmental props are navigable, for instance where we have narrow wooden walkways on the swamp palisade. The memory footprint is 621,084 KB (Working Set). This editor shot might better demonstrate why I need to optimize everything.



    I may go for a mix of navmesh in the non-constructable zones, and grid graph in the villagers where graph modification happens.

    The camera can go almost this high, right down to terrain level, and go anywhere, including inside buildings which will be set dressed with props.
     
    Last edited: Jun 22, 2012
  4. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,368
    I see yeah,
    Well in that case, i truly suggest you to try out Xaitment Map. Their navemesh system is powerful and better than the Unity one! It's also very scalable, supports very large terrains (and the license isn't that bad 500$, actually i think they have now both of their plugins XaitControl and XaitMap for 500$), it also have lods and you can recalculate the navigation at runtime if you wish (useful for your game style). I was one of the first to try it out and believe me, it is fast! It will solve all your navigation problems (including navigation agents, you can get rid of your character controllers that way) which is the most critical problem you are having now i guess. I think you can try it out for 30 days. The API is quite simple and you have more control over your navigation than any navmesh system available to Unity. If you want to save a lots of headhaches and time, try to get it. ;)
    Btw, your game is looking awesome, hopefully you will be able to finish it! Best wishes! :)
     
  5. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    Already tried it and provided feedback to them. With any navmesh, I'd have to make the mesh by hand as whenever I've tried various recast implementations it never copes very well. Admittedly that was when it first came out, so they may have done something about it.
     
    Last edited: Jun 22, 2012
  6. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,368
    Oh my,
    I'll try to find out the video where i saw that, but I'm sure you can compute the navmesh at runtime.
     
  7. Arges

    Arges

    Joined:
    Oct 5, 2008
    Posts:
    359

    Definitely - it got me looking at where I was still using the gameObject/rigidbody accessors, and doing a whole bunch of refactoring for pre-allocating some values.

    I'm curious: how many agents were you testing with, and with what behaviors? It's not obvious from the collapsed view on the screenshot. Also, were you using the UnitySteer version that's currently on the asset store?
     
  8. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    Interesting thread you have here.
    My A* Pathfinding Project is one of the discussed 3rd party tools. So here's some information on what is actually allocated during normal use.
    Basically it's the Path object (which is what you have highlighted in the second screenshot on the first page) and the path and vectorPath arrays (stored in the path object, these hold the calculated path as Node[] and Vector3[] representations). The first allocation I can do something about. I have actually tried, you can see the name of the function is GetFromPathPool. Though the path pooling turned out to too easily cause troubles if users tried to use the paths after they had been recycled (thinking they still contained the old info), I will try again to implement it in a better way.
    The second allocation, the arrays. That's basically set, if I don't store it as List<> objects and try to recycle those, but that would be quite messy I think.

    That's basic use, however I think path modifiers are the cause of the most allocations, especially the Simple Smooth modifier since it allocates a path with a much higher resolution than the original path. That allocation is quite hard to reduce. I don't really know how to remove it.

    Also, regarding other optimizations in the A* Pathfinding Project. Almost all optimizations you have brought up here are used to the fullest. Except the case of storing the array.length variable outside the loops. I did think that was optimized even in Mono, too bad it isn't.

    I would really like the awesome optimizations which some c++ compilers and to some extent java can do. Like optimizing this loop:
    Code (csharp):
    1.  
    2. int x = 0;
    3. for (int i=0;i<n;i++) {
    4.     x++;
    5. }
    to this:
    Code (csharp):
    1. int x = n*(n-1)/2;
     
  9. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    @Arges - around 50 active agents at one time I'd say using A*Pathfinding. However I finished up only using UnitySteer for two ( the ships ). It's probably an old version I was using as I haven't checked for an update in several months, so apologies if the tests highlight results that no longer exist - although from your posts it thankfully sounds like it was a useful exercise anyway for the non-cached transform. I've now migrated the ships to the List Graph in A*Pathfinding to reduce code dependencies.

    @sturestone - more of a feature request to consider reducing the allocations through recycling than reporting any bad behaviour in A*Pathfinding. I've tried nearly every pathfinding package out there over the last 14 months and A*Pathfinding is still the best for RTS games imho. You'll notice from the 1million graph nodes that allocation is a significant challenge for us. Ideally I'd use a mix of navmesh and grid graphs, but connecting all the points together at such high resolution would be painful given the editor lag that occurs when you move around (even with no graphs shown, but the gameobject with the AstarPath component added selected ), and the fiddly connection system ( at least it's fiddly when dealing with 1 million nodes ). Any chance of an auto-connect feature? I also make use of the Texture penalties for the preferred highways which would need to work with navmeshes if we were to use them, and that may rule out their use. All our characters have the SimpleSmoothModifier further compounding the issue.
     
  10. Arges

    Arges

    Joined:
    Oct 5, 2008
    Posts:
    359
    It probably was, but that's because the current version is still in development and likely to change, so I haven't made an asset store release. However, I very much appreciate it - I did find a few other cases where there were unnecessary allocations.

    For reference, I was running some performance tests, and with 100 tightly-packed together boids (which increases the elements on the radar for each), each one with 4-5 active steering behaviors, the current version allocates about 0.9K on the very extreme case of every agent updating its radar on every frame.

    These are likely mostly unavoidable now, since calls to Physics.OverlapSphere return an array (and we can't tell it to fill a pre-existing one), but can be significantly improved by a more reasonable approach of updating the radar only once or twice per second and staggering the updates, something that the queue settings on 2.5 allow.
     
  11. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    @Arges - UnitySteer is motion and steering though right, complimentary to, but not including, pathfinding. One thing I was unsure of was lets say an agent is on the edge of a cliff walking towards another agent. I can't remember if this would be an issue, with the local avoidance causing one agent to potentially route off the cliff? How do you normally go about integrating UnitySteer local avoidance with say A*Pathfinding?

    In my pursuit to improve performance in Folk Tale I've replaced the CharacterControllers with rigidbodies ( took about 30 mins mostly reconfiguring prefabs ) and there is a significant performance increase.

    I've also moved LOD fading away from using iTween and into custom shaders. Feels like a decent performance boost and removes some allocations, so definitely worth doing. Cleaner all round.
     
  12. Arges

    Arges

    Joined:
    Oct 5, 2008
    Posts:
    359
    That's a bit of a complicated question, since it depends on the case. Check out the world for Hairy Tales for reference:

    http://hairytalesgame.com/gallery/screenshots/

    As you can see, there's empty spaces all around them that they could fall into, but I also want them walking around items on the scene (such as them getting into position to pick up the stone). On this case, what I did was implement my own RVO approach, which acts as a post-processing steering behavior (a new concept on UnitySteer 2.5) and makes the empty areas undesirable (unless they're jumping to their death). The way it works then is that the main steering behaviors - path following, in your example - would decide where the agent wants to go to, while the post-processing ones correct it within certain parameters. My next project is likely to use UnitySteer even more than HairyTales, with more agents and more complex behaviors, so I expect I'll refine that further.


    Definitely, on my tests agents with CharacterControllers are about four times as expensive as those with just a Rigidbody+Collider.

    You may also want to consider Prime31's GoKit: https://github.com/prime31/GoKit


    By the way, if you're curious about the UnitySteer changes, here's a quick write-up: http://arges-systems.com/blog/2012/06/25/unitysteer-optimization-mobile-profiling/
     
  13. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    Now that's interesting...Unity's .rigidbody is more expensive than providing your own say .cachedRigidbody? I wonder if that is true for the other Unity variables like .audioSource and .collider. I'll have to run some tests and publish the results when I get some brain dead time.

    32K to 1K is good going on the optimization front. Maybe it's time I try the local avoidance in UnitySteer again, especially since I'm now running on rigidbody characters.
     
  14. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Yes, I believe that's true - all those properties at least used to just do GetComponent() internally. I think the .transform got changed in 3.5 to cache the component, but I don't think they've changed the rest.
     
  15. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    No, none of those are cached (not .transform either), they are the equivalent of using GetComponent.

    --Eric
     
  16. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    Why is that? For rigidbody and alike I can understand. But the transform component is always there, why is it not cached? It is a component accessed so often so I dont see any reason not to cache it for a, perhaps not large, or even very noticable, but still a performance improvement.
     
  17. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Not portable, more development effort, requires Pro, doesn't always actually gain enough speed to be worth it.

    --Eric
     
  18. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
  19. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    You seem to have used the generic version of GetComponent, which is slower than the non-generic version. In any case, caching transform is faster than using .transform; nothing changed in this regard in Unity 3.5.

    --Eric
     
  20. n0mad

    n0mad

    Joined:
    Jan 27, 2009
    Posts:
    3,732
    Oh crap ... :/

    *looks at all the generics to convert to non-generic*
    *take a shot of whisky*
     
  21. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Err... double check that. Last I checked the generic GetComponent is only marginally slower and the data I collected talks about an order of a magnitude difference.

    The source is there - so feel free to run the tests.
     
  22. n0mad

    n0mad

    Joined:
    Jan 27, 2009
    Posts:
    3,732
    I'd be interested in knowing that difference, even if it's measured in microseconds :)
     
  23. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    I can't do it now, got to go, but write something along the lines of:

    Code (csharp):
    1.  
    2. //untested
    3.  
    4. void Start(){
    5.  
    6. Transform trans;
    7.  
    8. var sw = System.Diagnostics.Stopwatch.StartNew();
    9. for (int i=0; i<1000000; i++) trans = GetComponent<Transform>();
    10. sw.Stop();
    11. print("GetComponent<> " + sw.elapsedMilliseconds + "ms");
    12.  
    13. var sw2 = System.Diagnostics.Stopwatch.StartNew();
    14. for (int i=0; i<1000000; i++) trans = GetComponent(typeof(Transform)) as Transform;
    15. sw2.Stop();
    16. print("GetComponent " + sw2.elapsedMilliseconds + "ms");
    17.  
    18. var sw3 = System.Diagnostics.Stopwatch.StartNew();
    19. for (int i=0; i<1000000; i++) trans = transform;
    20. sw3.Stop();
    21. print("Transform " + sw3.elapsedMilliseconds + "ms");
    22. }
     
  24. n0mad

    n0mad

    Joined:
    Jan 27, 2009
    Posts:
    3,732
    Thanks for the effort, I'd test that and post it here asap.

    result :

    Ok, nothing to be afraid of, thank you.
    (unit difference of 0.061 microseconds between generic and non-generic)
     
    Last edited: Jun 25, 2012
  25. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    Test Objective
    To confirm that Unity's Component inherited variables .rigidbody, .transform etc are not cached

    Case 1: Cached v Non-Cached Transform

    Code (csharp):
    1.  
    2.     public Transform cachedTransform;
    3.    
    4.     public void Update ()
    5.     {
    6.         NoCache ();
    7.         Cached ();
    8.     }
    9.    
    10.     public void NoCache ()
    11.     {
    12.         int i;
    13.         Transform newTransform;
    14.        
    15.         for ( i=0; i<100000; i++ )
    16.         {
    17.             newTransform = transform;
    18.         }
    19.     }
    20.    
    21.     public void Cached ()
    22.     {
    23.         int i;
    24.         Transform newTransform;
    25.        
    26.         cachedTransform = transform;
    27.        
    28.         for ( i=0; i<100000; i++ )
    29.         {
    30.             newTransform = cachedTransform;
    31.         }
    32.     }
    33.  
    Outcome:
    * Metrics include deep profile code
    - MonoBehaviour.transform takes 16.97ms ( 5.65 ms without deep profile )
    - Caching takes 0.3ms ( 0.3ms to 0.8ms without deep profile )

    $cpu-1-transform-inbuilt-deep.jpg


    Case 2: Cached v Non-Cached Rigidbody

    Code (csharp):
    1.  
    2.     public Rigidbody cachedRigidbody;
    3.    
    4.     public void Update ()
    5.     {
    6.         NoCache ();
    7.         Cached ();
    8.     }
    9.    
    10.     public void NoCache ()
    11.     {
    12.         int i;
    13.         Rigidbody newRigidbody;
    14.        
    15.         for ( i=0; i<100000; i++ )
    16.         {
    17.             newRigidbody = rigidbody;
    18.         }
    19.     }
    20.    
    21.     public void Cached ()
    22.     {
    23.         int i;
    24.         Rigidbody newRigidbody;
    25.        
    26.         cachedRigidbody = rigidbody;
    27.        
    28.         for ( i=0; i<100000; i++ )
    29.         {
    30.             newRigidbody = cachedRigidbody;
    31.         }
    32.     }
    33.  
    Outcome:
    * Metrics include deep profile code
    - MonoBehaviour.rigidbody takes 17.84ms ( 6.87ms without deep profile )
    - Caching takes 0.26ms ( 0.3ms to 0.8ms without deep profile )

    $cpu-2-rigidbody-deep.jpg


    Case 3: Cached v Non-Cached Audio

    Code (csharp):
    1.  
    2.     public AudioSource cachedAudioSource;
    3.    
    4.     public void Update ()
    5.     {
    6.         NoCache ();
    7.         Cached ();
    8.     }
    9.    
    10.     public void NoCache ()
    11.     {
    12.         int i;
    13.         AudioSource newAudioSource;
    14.        
    15.         for ( i=0; i<100000; i++ )
    16.         {
    17.             newAudioSource = audio;
    18.         }
    19.     }
    20.    
    21.     public void Cached ()
    22.     {
    23.         int i;
    24.         AudioSource newAudioSource;
    25.        
    26.         cachedAudioSource = audio;
    27.        
    28.         for ( i=0; i<100000; i++ )
    29.         {
    30.             newAudioSource = cachedAudioSource;
    31.         }
    32.     }
    33.  
    Outcome:
    * Metrics include deep profile code
    - MonoBehaviour.audio takes 18.83ms ( 7.64ms without deep profile )
    - Caching takes 0.36ms ( 0.3ms to 0.8ms without deep profile )

    $cpu-3-audio-deep.jpg

    I think we've established a pattern here so I won't run all the tests.


    Conclusion
    Valid observation.
    Component inherited variables are not cached.
    Caching references is strongly recommended.
     
    Last edited: Jun 26, 2012
  26. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Ah, interesting. I could have sworn Aras or Lucas told me at one point that they'd optimized it in 3.5, but thinking about it, maybe I'm misremembering and they were talking about caching some of the properties on a Transform (like position/rotation properties etc).
     
  27. n0mad

    n0mad

    Joined:
    Jan 27, 2009
    Posts:
    3,732
    Yup, interesting thanks.
    (anyway caching is generally recommended to avoid mem bandwidth hammering)
     
  28. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    Nice job n0mad. Filed that little gem away in the old gray matter should the need ever arise.
     
  29. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    What about other optimisation tips?

    E.g. using layers to identify types being faster than tags and names.

    Atlasing Textures, Combining Meshes, Batching, Static, Reducing Draw Calls ect?
     
  30. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,619
    There's plenty of places for people to find out about them, but this thread is about scripting/allocations/garbage collection, rather than content generation.
     
  31. jasonkaler

    jasonkaler

    Joined:
    Feb 14, 2011
    Posts:
    242
    It is possible that there is a direct internal reference (ie cache in this context) in unity but that the real overhead is swapping between mono and the unity engine.

    I would assume that mono referencing a unity object is slower than mono referencing a mono object.

    Anyway, it seems like the best practice is to
    a) cache references in script
    b) fetch references as seldom as possible
    c) allocate memory as seldom as possible
    d) Just a jump here back to that pathfinding tangent, if you have a huge list of stuff e.g. 1 million nodes, try use a hierarchy instead of a single collection. I personally would try use a high level graph linking areas, with seperate a graph within each area.
     
  32. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    I love how after that entire post you didn't actually check this.

    Even after sample code was posted.

    Even after contrary results were obtained and posted.
     
    Last edited: Jun 26, 2012
  33. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    Took me a moment to catch on there. Ok, so the observation of what is happening is incorrect and needs revising, but the overall conclusion that developers should cache references still stands...

    Code (csharp):
    1.  
    2.     public Transform cachedTransform;
    3.  
    4.     void Start()
    5.     {
    6.         Transform trans;
    7.        
    8.          
    9.        
    10.         var sw = System.Diagnostics.Stopwatch.StartNew();
    11.        
    12.         for (int i=0; i<1000000; i++) trans = GetComponent<Transform>();
    13.        
    14.         sw.Stop();
    15.        
    16.         print("GetComponent<> " + sw.ElapsedMilliseconds + "ms");
    17.        
    18.          
    19.        
    20.         var sw2 = System.Diagnostics.Stopwatch.StartNew();
    21.        
    22.         for (int i=0; i<1000000; i++) trans = GetComponent(typeof(Transform)) as Transform;
    23.        
    24.         sw2.Stop();
    25.        
    26.         print("GetComponent " + sw2.ElapsedMilliseconds + "ms");
    27.        
    28.          
    29.        
    30.         var sw3 = System.Diagnostics.Stopwatch.StartNew();
    31.        
    32.         for (int i=0; i<1000000; i++) trans = transform;
    33.        
    34.         sw3.Stop();
    35.        
    36.         print("Transform " + sw3.ElapsedMilliseconds + "ms");
    37.        
    38.        
    39.         var sw4 = System.Diagnostics.Stopwatch.StartNew();
    40.         cachedTransform = transform;
    41.        
    42.         for (int i=0; i<1000000; i++) trans = cachedTransform;
    43.        
    44.         sw4.Stop();
    45.        
    46.         print("Cached Transform " + sw4.ElapsedMilliseconds + "ms");
    47.    
    48.     }
    49.  
    GetComponent<> 663ms
    GetComponent 592ms
    Transform 54ms
    Cached Transform 2ms
     
  34. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Oh. Well that makes it look like .transform is cached on Unity's end, and the 54ms is managed<->native transition overhead.
     
  35. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    That was not disputed, what I did dispute was that transform was not the same as GetComponent<Transform>() /GetComponent(typeof(Transform) as Transform. This is borne out by the factor of 10 speed differance in these simple tests.

    I have an entire thread about the idea of caching components, and while I do agree there may be benefits:

    • You are using more memory, and making code more complex.
    • There are issues around ensuring the cache is accurate - most components can be added/removed during runtime [transform is an exception AFAIK].

    I personally think the concept of caching references often suffers from premature usage - where one focuses on one type of efficiency [saving a few ns per frame] and forgets about others [e.g. the dev time maintenance of the extra LOC]. Most of the time I believe the the tradeoff isn't worth it.
     
    Last edited: Jun 26, 2012
  36. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    no offence but made any mobile games recently that run at 60fps? I have, and believe me you need to be anal about caching. End of.

    While I'm usually mr practical and I don't care much for wasting my time on useless efforts, recommending that people don't cache stuff or even implying it is pretty bad advice for mobile.

    Let's look at functions which can get called several times per frame per object, such as collisions, and there becomes a real need to do so even if the object count is relatively low.

    You know that, I know that, but I'm posting this for forum ref.
     
    Last edited: Jun 26, 2012
  37. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    Let's try to keep the discussion away from speculative opinion and focus on discussing empirical evidence and points for consideration.

    Anyone got any other interesting code optimizations that are worth testing?
     
  38. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Nope, I didn't know that was the only type of application one could release with unity :p

    Seriously though, what performance gains did you get - and how many calls did you cache to get those improvements?

    What did I say?

    I personally think the concept of caching references often suffers from premature usage - where one focuses on one type of efficiency [saving a few ns per frame] and forgets about others [e.g. the dev time maintenance of the extra LOC]. Most of the time I believe the the tradeoff isn't worth it.

    I do not limit myself to mobile, nor do I say caching is bad. What I do do is highlight the negative side of the equation that people often forget. I also maintain it's my opinion based on my experiences. It's also important to note that this advice is based around the .transform variable that is already cached and is an order of a magnitude faster than GetComponent.
     
    Last edited: Jun 26, 2012
  39. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,368
    The Transform component is already cached? I don't think so.
     
  40. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    If .transform isn't cached, how do you explain the fact that it's ten times faster than GetComponent?
     
  41. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    It's slower than manually storing transform in a variable, so I guess it's semi-cached. ;) What is it actually doing behind the scenes?

    --Eric
     
  42. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,368
    I think .transform gets cached the first time you use it (not quite sure though), while GetComponent<T>() actually get the reference every time you use it (slower).
    The best thing to do is cache the .transform in a variable prior to use it (that's what i do and i found it even faster than the so called "cached" .transform itself). ;)
     
    Last edited: Jun 27, 2012
  43. lilymontoute

    lilymontoute

    Joined:
    Feb 8, 2011
    Posts:
    1,181
    This. Caching the transform as a local variable is faster than storing it as a field in a class and caching it on initialization. There was some test of this somewhere on the forums, actually.
     
  44. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    Based on the results of the local v member variable performance test, this would make sense.
     
  45. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,368
    Sorry,
    What I mean was that storing .transform in a variable prior to use it (eg: On Awake/Start) is actually faster than using .transform directly. Made several bunch of test and is faster (but only at the beginning).
    [EDIT]
    Also, i can confirm (as you previously mentioned) that is also faster than using a variable of Transform as a public field. The reason is that it does exactly the same thing as .transform (it gets cached only the first time you use it).
    [EDIT2]
    Wondering if using .transform will make the CG work a lot more? Imagine that the transform gets cached only when you use it the first time, but what if the CG deletes his reference after a little while and then when you call .transform it gets again cached? This will results in some intermittent hickups. While i cannot certify that's the behavior of things, i know that caching the .transform in a variable made my game a lot faster and lots of hickups dissipated mysteriously. xD
     
    Last edited: Jun 27, 2012
  46. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,619
    Why would the GC delete your reference? The GC's job is to delete data once all references to it are removed, not to go around randomly nerfing your references. ;)
     
  47. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,368
    I'm perfectly aware of what's the GC does. xD
    But what if .transform reference is used/cached only temporary? It then gets wiped out by the GC later and then when you try to use it the next time, it gets cached again. I'm not exactly sure what's really happens when you use .transform but why storing it in a variable is faster than simply use .transform?
    Those are simply some small reflections/assumptions of what could possible be happening when you use .transform. :p
     
    Last edited: Jun 27, 2012
  48. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,657
    Ah, I see. We're talking about different 'caching' situations: caching it ourselves on the managed side (which is by far the fastest option), versus Unity caching it on the native side (still faster than GetComponent, but a lot slower than managed-side caching due to the overhead of native<->managed transitions).
     
  49. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,368
    Yes ;)
     
  50. Games-Foundry

    Games-Foundry

    Joined:
    May 19, 2011
    Posts:
    632
    An interesting, uncommon and possibly pointless architectural test here. While there are huge benefits to events that aren't covered here - such as any object being able to become a subscriber - I wanted to test if it would outperform class inheritance overrides, and thus provide an alternative architectural model with better performance.

    If this is of interest to other developers, perhaps they can advise on more appropriate test code?

    Test Objective
    To test the performance of self-targeted events v. inheritance-based overrides.

    Code (csharp):
    1.  
    2.     public GoldenRetriever goldenRetriever;
    3.    
    4.     public void Awake ()
    5.     {
    6.         goldenRetriever = gameObject.AddComponent<GoldenRetriever>();
    7.     }
    8.    
    9.     public void Start ()
    10.     {
    11.         // Event based calling
    12.         var sw1 = System.Diagnostics.Stopwatch.StartNew();
    13.         for ( int i=0; i<10000000; i++ ) goldenRetriever.DoBarkEvent ();
    14.         sw1.Stop();
    15.         print( "Event based calling " + sw1.ElapsedMilliseconds + "ms" );
    16.         print( "Counter->" + Dog.counter );
    17.        
    18.         // Override based calling
    19.         var sw2 = System.Diagnostics.Stopwatch.StartNew();
    20.         for ( int i=0; i<10000000; i++ ) ( goldenRetriever as Dog ).DoBarkOverride ();
    21.         sw2.Stop();
    22.         print( "Override based calling " + sw2.ElapsedMilliseconds + "ms" );
    23.         print( "Counter->" + Dog.counter );
    24.     }
    25.  
    Code (csharp):
    1.  
    2. public class Dog : MonoBehaviour
    3. {
    4.     public static event Action Bark;
    5.     public static int counter;
    6.    
    7.     public virtual void DoBarkOverride ()
    8.     {
    9.     }
    10.    
    11.     public void DoBarkEvent ()
    12.     {
    13.         if ( Bark != null )
    14.             Bark ();
    15.     }
    16. }
    17.  
    Code (csharp):
    1.  
    2. public class GoldenRetriever : Dog
    3. {
    4.     public void Awake ()
    5.     {
    6.         Dog.Bark += OnBark;
    7.     }
    8.    
    9.     public void OnDestroy ()
    10.     {
    11.         Dog.Bark -= OnBark;
    12.     }
    13.    
    14.     // Event handler
    15.     public void OnBark ()
    16.     {
    17.         counter++;
    18.     }
    19.    
    20.     // Inheritence override version
    21.     public override void DoBarkOverride ()
    22.     {
    23.         counter++;
    24.     }
    25. }
    26.  

    Outcome:
    Inheritance-based overriding appears at first glance to be consistently quicker than self-targeted events.

    Event-based calling 145ms
    Override-based calling 80ms
     
    Last edited: Jun 28, 2012