Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Latest performance tips

Discussion in 'Scripting' started by Ziron999, Sep 17, 2015.

?

Was this information helpful?

  1. Yes

    4 vote(s)
    36.4%
  2. No

    7 vote(s)
    63.6%
  1. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    I would like to provide critical information that a lot of people don't seem to understand...At the end of the day a computer just processes numbers(Math) there is an order in which everything is executed. Here is unity's engine.

    Avoid = don't use whenever possible...

    Unity order of operation:
    All Awake calls
    All Start Calls
    while (stepping towards variable delta time)
    All FixedUpdate functions
    Physics simulation
    OnEnter/Exit/Stay trigger functions
    OnEnter/Exit/Stay collision functions
    Rigidbody interpolation applies transform.position and rotation
    OnMouseDown/OnMouseUp etc. events
    All Update functions
    Animations are advanced, blended and applied to transform
    All LateUpdate functions
    Rendering

    Now with that in mind it is good to use delegates & events as much as possible
    Say you have 10 scripts that have Input commands. It is recommended to create a singleton.
    Example:
    Code (CSharp):
    1. public delegate void SingleInputDelegate();
    2. public static event SingleInputDelegate OnInput;
    3.  
    4. void LateUpdate()
    5. {
    6. if (OnInput != null)
    7. OnInput();
    8. }
    the reason for this is because of the way the memory is stored it will take all of your input code and place it into one section. I use lateupdate for most things because then other things can work correctly. It is kinda stupid just how fast delegates are! Use a profiler for proof.

    In each of the 10 scripts you just do this in the Awake() class and create an input class:
    Code (CSharp):
    1. void Awake() { SIngleton.OnInput += InputInfo; }
    2. void InputInfo() { // input info goes here if (input.blahblahblah }
    events can be used for far more then just input this is just a good example. The main thing you will want to pay attention to here is the order of script execution starts to matter a lot. To get you to think about it ask yourself this. Which awake is called first? Remember everything happens in order of execution it does NOT happen all at once.

    Avoid using Lists as much as possible. However this does depend on what you are doing. It is always best to do object pooling with an array instead like so:
    Code (CSharp):
    1. private GameObject PlayerUnit;
    2.     private GameObject[] PlayerUnitCache = new GameObject[GlobalLimit];
    3.     private NavMeshAgent[] PlayerAgentCache = new NavMeshAgent[GlobalLimit];
    4.     private HealthSystem[] PlayerHealthCache = new HealthSystem[GlobalLimit];
    5.  
    6. void Awake()
    7.     {
    8. PlayerUnit = Resources.Load<GameObject>("PlayerUnit") as GameObject;
    9.     }
    10. for (int i = 0; i < GlobalLimit; i++)
    11.         {
    12.             GameObject playerUnit = Instantiate(PlayerUnit) as GameObject;
    13. //only if you need a parent            playerUnit.transform.parent = canvasRectT.transform;
    14.             playerUnit.SetActive(false);
    15.             PlayerUnitCache[i] = playerUnit;
    16.             PlayerAgentCache[i] = playerUnit.GetComponent<NavMeshAgent>(); //or PlayerUnitCache[i] instead of playerUnit doesn't matter since it's a cache during awake
    17.             PlayerHealthCache[i] = playerUnit.GetComponent<HealthSystem>();
    18.         }
    The benefits to this is you never call destroy/instantiate. You simply just do .setactive(false)/.setactive(true) which is just a Boolean check. destroy/instantiate create and destroy the ENTIRE gameobject.

    Sadly in a case where there is more then one variable dependent on one another. Even a Dictionary is faster then a List. That is just how bad lists are. Only use lists in cases where it is just 1 thing that needs to be handled that will be removed/added throughout the game but again putting a cache array limit is always going to be better. Even with bullets I do int maxBulletsAllowed; as the cache variable.

    Keep NavMeshAgents from COLLIDING!
    Navmeshes are a beautiful thing but do not let the agent's collide. For example in an RTS you will have to create formations or a flock system or they will all keep fighting to get to the same spot causing a lot of physics calculations. Create a distance calculation to stay away from the enemy. Which leads me to the next performance optimization.

    Never ever use Vector3.Distance use sqrmagnitude instead!
    Code (CSharp):
    1. if (target != null)
    2.         {
    3.             distance=(transform.position-target.position).sqrMagnitude;//Vector3.Distance(transform.position,target.position);
    4.             if (distance > distanceMax)
    5.                 //agent.SetDestination(target.position);
    6.                 transform.position = Vector3.MoveTowards(transform.position, target.position, Time.deltaTime);
    7.             else
    8.                 return; //needs to be unit moving not: startedMoving = false;
    9.         }
    I'm not sure on the backend code for this but you can see it with the profiler. sqrmagnitude is faster then vector3.distance and you can get the same result in every case I've had to do a distance check in so far.

    Do not ever used foreach I have no idea why you ever would want to.
    Code (CSharp):
    1. foreach (GameObject go in PlayerUnit)
    2. { go.blahblah }
    this is faster just do this
    Code (CSharp):
    1. for (int i = 0; i < PlayerUnit.Length; i++)
    2. { PlayerUnit[i].blahblah }
    It's literally the same for's are just faster period.

    for's inside of for's are horrible on performance you can do it but I would try to find alternative in every case. However an alternative may not be possible depending on the case.

    ADDED:
    Never use sendmessage. You can send a message to a private void but with the performance hit you get just make the class public and bam done. This is another one I see no reason to use...it's a pretty high performance hit.
    Example:
    Code (CSharp):
    1. UnitControl tempcontrol = SelectableObject.GetComponent<UnitControl>();
    2. tempcontrol.OnUnselected(); //.SendMessage("OnUnselected", SendMessageOptions.DontRequireReceiver);
    Added 2:
    - Avoid using Invoke or StartCoroutine with a string. //strings are bad
    Unfortunately due to how unity has stopcoroutine working. You will have to use a string in certain cases.

    - Avoid using GUILayout and flag your GUI MonoBehaviour to prevent the 800bytes/frames of GUI garbage from occurring. (http://docs.unity3d.com/ScriptReference/MonoBehaviour-useGUILayout.html)
    Luckily due to the new UI this info isn't as relevant as previous versions of unity.

    - Avoid using GameObject.Tag or GameObject.Name use layers as much as possible //strings are bad.
    Integer's process faster then strings by quite a bit so use layers instead of tag's or names when possible. There is only so many layers so of course use main commonly used things as layers the most. This is also one of the reasons sendmessage is bad.

    - Avoid using GetComponent in Update, and cache if possible. If you do have to use it in update and it's called more then once call a local getcomponent and use that.
    Example:
    Code (CSharp):
    1. void Update { PlayerDamage playerDamage = Targets[0].getcomponent<PlayerDamage>(); if (blah blah)playerdamage.blah blah; else if (blah blah blah) playerdamage.blah blah blah }
    The reason being is you only have to call get component once this way instead of multiple times. If you only have 1 call then it makes little to no difference either way. So as a rule of thumb I just create the local variable when needing to do this no matter what. That way if for whatever reason in the future you can just use it again and it's already optimized as much as possible.

    - avoid using OnTriggerEnter, OnCollisionEnter, ect. Try to use Physics.sphereoverlap instead whenever possible.
    The reason for this is OnTrigger/OnCollision are actually based on PhysX which is a performance hit especially when you go with mobile platforms.

    - Create local information about the gameobject your script is in.
    Code (CSharp):
    1. Transform thisTransform;
    2. void Awake() { thisTransform = transform; }
    Now this one I don't know the reason behind why it's faster. If someone has that additional info please explain.

    here is a basic blank starting Singleton script:
    Code (CSharp):
    1. public class Singleton : MonoBehaviour {
    2.     public int BaseHealth = 9999;
    3.     public int BaseMaxHealth = 9999;
    4.  
    5.     private static Singleton instance = null;
    6.     public static Singleton Instance
    7.     {
    8.         get
    9.         {
    10.             if (instance == null)
    11.             {
    12.                 instance = (Singleton)FindObjectOfType(typeof(Singleton));
    13.             }
    14.             return instance;
    15.         }
    16.     }
    17.  
    18.     public bool destroySelf = false;
    19.    
    20.         if (Instance != this)
    21.         {
    22.             Destroy(gameObject);
    23.         }
    24.         else
    25.         {
    26.             if (destroySelf == true) destroySelf = false;
    27.             DontDestroyOnLoad(gameObject);
    28.         }
    29.     }
    30. }
    Singletons are something you have to be very careful with. Yes it is the ultimate convenience tool but it can suck up all your memory easily too. As an example I left basehealth in it to show how to use it. You simply in any script at anytime anywhere do Singleton.Instance.BaseHealth = whatever; Singleton scripts are by far the best place to put all of your events/delegates, other static variables, non-static global variables, & enum's! This gives you the ability to easily organize your entire process to insure things go smoothly.

    I should note that if your game is small and basic enough these tips I'm giving don't really matter. It would still be faster but the game will most likely run fine without them.
     
    Last edited: Sep 19, 2015
  2. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    You completely missed the point in several areas, but the information is generally good.

    Dictionaries have always been faster than lists, however they cant be accessed via index, which means lists/arrays/dictionaries all have there place and time.

    Direct lookup (key) with no iteration: Dictionary
    Direct Access (index) with a static size: Array
    Direct Access with a dynamic size: List

    foreach is avoided because it creates garbage for the GC with the enumerator. Its is perfectly valid choice to use it and preferred in some cases. You not understanding why people would want to use it is not a valid reason to avoid it. A few sporadic calls is fine. Done in Update is bad.

    Not sure why you even bothered mentioning this since you offered zero solution or reason for avoiding (other than being visually acceptable)

    Your arrays of components are good, however dictionaries are better suited, unless you intend to iterate over the entire collection, in which case I would probably go with Lists, unless I was certain my limits would never be reached, then I would choose array.

    Code (csharp):
    1.  
    2. Dictionary<int, GameObject> playerUnits;
    3. Dictionary<int, NavMeshAgent> playerAgents;
    4.  
    5. GameObject playerUnit = Instantiate(PlayerUnit) as GameObject;
    6. playerAgents.add(playerUnit.root.GetInstanceID(), playerUnit.GetComponent<NavMeshAgent>());
    7.  
    8.  
    The reason I prefer to do it this way is so that say I have a collision, from that I can get the gameObject, from that I have the ID so I can access any component directly without having to iterate over the array to find a match.

    Although you could argue that you should just use GetComponent in this situation.
     
  3. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    ^ good info!

    I did mention an example just not a script example...you need to use a flock or formation system. unfortunately there is so many ways this would be difficult to show an example in a short amount of information. Basically, this one subject really could be a post in itself.

    Instantiate and Destroy are both performance hit's as well that's why you object pool? I did forget to mention that though so that makes sense.

    I see your point but that's why I showed in the example that you object pool the components as well. This way you never call getcomponent. However, if it is a small amount of objects I guess this would be fine but if you are doing 200-1000 objects constantly this is where you notice a massive performance difference. Unfortunately, even with dictionary I have not found a good way to "search" all searching appears to be bad no matter which way you do it but if I was to search dictionary is definitely the best way.
     
    Last edited: Sep 17, 2015
  4. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    If someone has an example of a reason to use foreach over for's I'd be interested in seeing it. Maybe there is something I just don't know here?
     
  5. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    Fair enough on the flocking/avoidance stuff. Its definitely a subject in its own right.

    Instantiate is unavoidable, but Destroy is where pooling shows its strength. Keeping references to individual components isn't object pooling. I would call that reference pooling, which doesnt actually offer up much benefit except for potential ease of access.

    The note about using sqrmagnitude is a good one. I read something a while back that said using maths directly rather than the helper functions is generally preferred for small performance improvements (like adding vectors, distance (as you mentioned), etc).

    One thing I'm not certain on now is performance of getcomponent. I understand its been improved, but how much I havnt tested. I would generally avoid it as you mention in situations where you have lots and you want to constantly access them.

    The use of foreach is a personal preference thing. Its generally easier/nicer to maintain, but considering its downsides, I generally only use for loops. Im working on an editor script where I use foreach as consideration of the GC is irrelevant for editor scripts.
     
  6. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Dictionaries aren't faster than List, they're slower (depending on what you're doing), and are for quite different things anyway. Lists are backed by arrays (which are the fastest collection), though there's some overhead which makes them slower. How much depends on what the type is; for something like GameObject, there's hardly any difference. "Avoid List" isn't good advice, sorry.

    --Eric
     
    Ryiah likes this.
  7. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    If you have to iterate over a list to find an object rather than direct access via key (dict), I would expect that to be slower. Im sure that's what you meant of course ;)
     
  8. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    ya dictionary is certainly faster at searching hands down :) but this is something you can easily prove to yourself with the profiler just make a 1million or something ridiculous list and dictionary and search them both. You'll see it easily.
     
    Last edited: Sep 17, 2015
  9. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    Because it is a getcall vs it already being in memory and it just being the next iteration from the array cache from my understanding. Most of my information was from extensive testing with the profiler. Other parts are just from knowing how it's being called like you are stating James. However you are better at that part then me lol
     
  10. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    Without access to engine source I dont know the functionality behind getcomponent. Either way, I avoid using it as much as possible
     
  11. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Bad advice

    Bad advice

    Bad advice

    Bro, do you even code?
     
    alexzzzz, Ryiah and Korno like this.
  12. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    these are performance tips(thus the title?)...if you want speed you do not want to use those. You must be one of the people who just codes however you feel like it which is the whole point of these performance tips lol
     
  13. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I suppose I should offer some more defence of my position.

    In general your post is very hand wavy and doesn't provide a huge amount of context. Almost every coding technique has a proper time and place to be used. You've thrown out a bunch of tips that sometimes work and sometimes won't.

    In general developer performance is an important consideration. Discussing performance doesn't make any sense without a reference to the profiler and identifying hot code..

    On to specifics:

    Here you describe hooking up all of your logic to a C# event that is called in one LateUpdate on a single MonoBehaviour. That's a lot of boiler plate code I need to write for every class. I would have to check the profiler, but it wouldn't surprise me if Unity already does something similar internally.

    Lists aren't that bad. A list is essentially an array with a couple of wrapper methods to do some common task, like resizing and adding an element in the middle. Sure if you identify hot code that is creating a major performance bottle neck, then use an array instead of a list. But most of the time its not going to make an appreciable difference.

    I'm not sure what the point of mentioning a Dictionary here was. Dictionaries (and Hashsets) are different collections for different purposes. Comparing it to a List for speed is like comparing a boat to a car. Sure they have different max speeds. But the max speed is not the reason to choose between a boat and a car.

    Generally valid for exceptionally high numbers of agents. But if you really want high performance for an RTS you would be better off ditching the agent based system altogether.

    This is valid. In most cases working in squared space is better. There are some cases you will need to revert to using distance.

    Again, it depends on your use case. The hoops to jump through to do a iterative for loop on a Hashset or Dictionary would hardly be worth the slight gain in GC performance.

    Nested loops have no inherent performance issue. You are simply executing more code. Ten thousand iterations of a for loop will perform the same as two nested loops of one hundred iterations each.

    SendMessage has a very specific purpose. Its used to communicate between scripts without requiring dependency between the scripts. While its largely been replaced by ExecuteEvents, there are still some places where SendMessage is relevant. GetComponent does a different job, its used for communication between scripts that have dependency.
     
    Korno likes this.
  14. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    You do typically want to use List though. Also creating a singleton doesn't have much to do with performance. SendMessage can be used when you don't know and don't care what components are attached. Also, micro-optimizations never really make sense, since it's likely that all of the "performance" code you write will eventually end up getting replaced anyway. Yes you should code "how you feel like it" (or rather, in a sensible way), so you can read your code, to avoid bugs. In general you should do "performance" stuff last, after you've profiled.

    --Eric
     
    Ryiah and Kiwasi like this.
  15. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    I am sorry but this is just crap. Of course if you are doing nested loops, you are executing more instructions but this does not imply slowness. There is no sudden drop in performance because you have a for inside a for....

    Code (CSharp):
    1. for(int j=1; j<100; j++ )
    2.                 {
    3.                     for(int k=1;k<100;k++)
    4.                     {
    5.                         Debug.Log(k * j);
    6.                     }
    7.                 }
    8.  
    9.                 for (int i = 0; i < mActiveTracks.Count; i++)
    10.                 {
    11.                     mActiveTracks[i].SuperLongComplicatedMethodUsePrimeNumberGeneration();
    12.                 }
    Which is slower?

    Apart from that, some reasonable advice.
     
  16. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Now that I've said my piece about the content, I'm curious about the story behind this post. What lead you to these conclusions?

    Reading between the lines it sounds like you started making an RTS by piling a ton of stuff onto each agent and then duplicating that agent 200+ times. That way typically leads to performance issues.

    Generally to write performant code you want to start at the top end and work down. Do you have the right systems in place, can any systems be removed? What code is called most often, can it be called less? Are there more efficient algorithms that can be used? Only once you get to this stage is it worth considering can I make my data structures and method calls more efficient. And typically you would only need to do this on a very small part of your program.
     
    Ryiah likes this.
  17. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    I agree with @BoredMormon here.

    Also, if was making an RTS that has a high number of units I might consider using Data Orientated Design for the critical paths - Unit movement, Updates and AI etc.
     
    Kiwasi and Ryiah like this.
  18. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    I said that sometimes for's inside of for's cannot be avoided but if you can it is a good idea to do so. I didn't say never do them???
     
  19. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    With object pooling there is no removing. You just set them inactive. That would be the point you don't load or destroy an entire object this way. What lead me to these conclusions is the profiler I've mentioned many times you can do these tests yourself just use a profiler and you can see yourself if you find faster methods i'd be interested in seeing an example. I'd like to see a proven case where any of what I say is NOT the fastest method I would totally be willing to change what I'm saying.
     
  20. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    DOD is a weird one. I've been considering using a similar approach for 3D water physics for one of my Pond Wars projects. The in built physics system just isn't built for what I want to do.

    I'd be curious to see if anyone's done something similar with Unity. I have the feeling you'd be fighting C# and Unity most of the way down.
     
  21. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    I completely agree in fact before I started profiling I was using things like list and foreach. I've never seen a reason to use sendmessage yet though. When I started profiling these are the results I found. That's why I figured it would be good advice to throw out for people so they don't have to spend as much time profiling as I did. Plus I would like to see if someone has a performance tip I don't know that would be awesome. Data-oriented programming is a good tip...kinda except OOP really is the future so in a way that won't matter. Don't see OOP being replaced by data programming any time soon.
     
  22. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    Nobody doubts object pooling is important. In fact on my mobile endless runner (see signature) I create game objects at the start then pool nearly every object that is possible (track, obstacles, pickups, particle effects).

    Nobody disagreed with you on that point.

    BoredMormon is making the point that these micro optimizations mean nothing if your actual logic or setup is inefficient.

    The example of an RTS - If you have 200 units that have 10 components on them all with update/fixedupdate\agents etc. Unwrapping a nested for loop or using an array instead of a list is not going to fix that performance. You need to consider your overall design first.

    Then, if you still have a slow portion of code, then and only then, you can consider micro optimizations.
     
  23. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    Yeah this is the reason I haven't tried it in Unity. If the game engine isn't designed from the bottom up with it in mind then I don't think you are going to have much success.

    The only way I think you could do something is maybe have a game object with one Update that moves a ton of other game objects that it manages, and culls the dead ones etc instead of each game object doing it itself. But those objects will still actually exists as instances not just an index into some data so not sure if would actually improve anything much.

    Also, I am not sure I like DOD that much. Maybe for lower level engine code but for logic it seems like you are throwing away a lot of the benefits of OOP. and reuse. I suppose for bleeding edge stuff where every cycle counts its good.
     
    Kiwasi likes this.
  24. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Since my last post was pretty much a detailed break down about most of my concerns with your logic, I think I'm done here.
     
  25. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    depends on the mactivetracks.count
    also if you want to really "see" the performance difference add more 0's. do like 1000000 in each and you will see it easily. Just go into unity and do windows->profiler it works in the free version now.

    Oh and yes...executing more instructions DOES imply slowness. Again I'd like to see an example where it doesn't, this isn't one. This is just one of the cases I said there is no other way around it. You still want to avoid "whenever possible" that does not mean don't ever do it. Sometimes you can get one of the other variables an entire different way. It's hard to show an example of this because it is case by case you cannot always avoid nested loops. I've mentioned this like 3x now. Jeez man your personality is a tad bit brutal lol
     
    Last edited: Sep 18, 2015
  26. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    Sorry I didn't mean to come across like that! Please accept my apologies! :oops: I do disagree with you though but its dependent on what is happening in the loops not that it is nested or not nested.
     
  27. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    actually the personality bit I was talking about boredmormon sorry korno my fault for not saying who!
     
    Korno likes this.
  28. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Sorry for coming across brutal. I think it comes from talking to computers as much as I talk to people. Computers don't apeciate the nuances of politeness.
     
    Rostam24 likes this.
  29. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    the first thing I mentioned is the order of operation unity uses lateupdate is towards the end this prevents your animation and other things from having any issues that's why I recommend it.

    I should just get rid of this out of performance tips everything seems to think I mean never use it.

    That's right, laziness at the cost of performance!

    I have been reading around on the net to find information to backup what I'm trying to say because I apparently am not that good at it. It looks like stackexchange is really good and can show why lists, foreach, & sendmessage are worse then the alternatives.
     
  30. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    So what's your proposed solution to get proper decoupling without using SendMessage or ExecuteEvents? Say a bullet that needs to communicate damage to an object it's just hit. Without knowing in advance how many components in that object can recieve damage?

    It's not lazy coding. It's about using the right tools for each job at hand. And only micro optimising on hot code. In many applications developer time is more precious then a couple of CPU cycles.
     
  31. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    all of those components should be cached so they can be called on. Example in the main parent script you make a gameobject array with all of the children. I write notes to know which one is which..

    // GameObject[0] = playerdamage or whatever

    of course you could just skip gameobject all together it depends on what you need to cache

    // PlayerDamage playerDamage = GetComponentInChildren
    // CollisionInfo collisionInfo = Getcomponentinchildren
    and so on. All of this in the main parent for every object have a script that can be called. This is how you get information from an object effectively. Global commonly used things should go into a singleton. Like the playerdamage amount. This will make things infinitely easier when going to do things like upgrades to change damage too. :)
     
    Last edited: Sep 19, 2015
  32. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    In my opinion SendMessage is never the right tool. This is not because of performance reasons or anything like that, but because of maintainability. The same is true for anything else that uses string based calls.
    The most difficult part of it is, that it can fail only because a method was renamed or because of a typo. Delegates are far more maintainable, because everything would be renamed automatically or you would get a compile time error in the case of a typo. When using SendMessage, you may get an error message early on if you are lucky. If there is no error message and you are not directly seeing that something is wrong, there may be fine times ahead to find an easily avoidable issue.
     
    Kiwasi and Korno like this.
  33. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    I was curious so I did a test.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Diagnostics;
    3. using System.Collections.Generic;
    4.  
    5. public class TestGetComponentSpeed : MonoBehaviour
    6. {
    7.     NotAMonobehaviour[] array = new NotAMonobehaviour[30];
    8.     List<NotAMonobehaviour> list = new List<NotAMonobehaviour>();
    9.     Dictionary<int, NotAMonobehaviour> dictionary = new Dictionary<int,NotAMonobehaviour>();
    10.     int iterations = 1000000;
    11.  
    12.     void Start()
    13.     {
    14.         //Is 2nd component on gameobject (right under transform)
    15.         TestGetComponentOne(); //result = 00:00:00.1410046 (~141 Milliseconds)
    16.  
    17.         //Is 30th component on gameobject
    18.         TestGetComponentTwo(); //result = 00:00:00.4986637 (~497 Milliseconds)
    19.  
    20.         TestArray(); //result = 00:00:00.0025996 (~2.6 Milliseconds)
    21.         TestList(); //result = 00:00:00.0089982 (~9 Milliseconds)
    22.         TestDictionary(); //result = 00:00:00.0329073 (~33 Milliseconds)
    23.     }
    24.  
    25.     void TestGetComponentOne()
    26.     {
    27.         Stopwatch stopWatch = new Stopwatch();
    28.         stopWatch.Start();
    29.  
    30.         for(int i = 0; i < iterations; i++)
    31.             GetComponent<ComponentOne>().value = 5;
    32.  
    33.         stopWatch.Stop();
    34.         UnityEngine.Debug.Log(stopWatch.Elapsed);
    35.     }
    36.  
    37.     void TestGetComponentTwo()
    38.     {
    39.         Stopwatch stopWatch = new Stopwatch();
    40.         stopWatch.Start();
    41.  
    42.         for(int i = 0; i < iterations; i++)
    43.             GetComponent<ComponentTwo>().value = 5;
    44.  
    45.         stopWatch.Stop();
    46.         UnityEngine.Debug.Log(stopWatch.Elapsed);
    47.     }
    48.  
    49.     void TestArray()
    50.     {
    51.         array[29] = new NotAMonobehaviour();
    52.  
    53.         Stopwatch stopWatch = new Stopwatch();
    54.         stopWatch.Start();
    55.  
    56.         for(int i = 0; i < iterations; i++)
    57.             array[29].value = 5;
    58.  
    59.         stopWatch.Stop();
    60.         UnityEngine.Debug.Log(stopWatch.Elapsed);
    61.     }
    62.  
    63.     void TestList()
    64.     {
    65.         for(int i = 0; i < array.Length; i++)
    66.             list.Add(new NotAMonobehaviour());
    67.  
    68.         Stopwatch stopWatch = new Stopwatch();
    69.         stopWatch.Start();
    70.  
    71.         for(int i = 0; i < iterations; i++)
    72.             list[29].value = 5;
    73.  
    74.         stopWatch.Stop();
    75.         UnityEngine.Debug.Log(stopWatch.Elapsed);
    76.     }
    77.  
    78.     void TestDictionary()
    79.     {
    80.         for(int i = 0; i < array.Length; i++)
    81.             dictionary.Add(i, new NotAMonobehaviour());
    82.  
    83.         Stopwatch stopWatch = new Stopwatch();
    84.         stopWatch.Start();
    85.  
    86.         for(int i = 0; i < iterations; i++)
    87.             dictionary[29].value = 5;
    88.  
    89.         stopWatch.Stop();
    90.         UnityEngine.Debug.Log(stopWatch.Elapsed);
    91.     }
    92. }

    I am not sure as to what situation you were meaning when saying that, but according to my test code above, list is much faster then a dictionary.

    In visual studio, when pressing f12 on a "transform" it shows me that it is using a Getter. ILSpy shows this
    Code (CSharp):
    1. public extern Transform transform
    2. {
    3.     [WrapperlessIcall]
    4.     [MethodImpl(MethodImplOptions.InternalCall)]
    5.     get;
    6. }
    Thats probably where the extra delay is.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Diagnostics;
    3.  
    4. public class TestTransformSpeed : MonoBehaviour
    5. {
    6.     Transform myTransform;
    7.     Transform myTransformGetter {get {return myTransform;}}
    8.     int iterations = 1000000;
    9.  
    10.     void Start()
    11.     {
    12.         myTransform = GetComponent<Transform>();
    13.  
    14.         TestTransform(); //result = 00:00:00.0923923 (~92 Milliseconds)
    15.         TestMyTransform(); //result = 00:00:00.0694768 (~69 Milliseconds)
    16.         TestMyTransformGetter(); //result = 00:00:00.0739161 (~74 Milliseconds)
    17.     }
    18.  
    19.     void TestTransform()
    20.     {
    21.         Stopwatch stopWatch = new Stopwatch();
    22.         stopWatch.Start();
    23.  
    24.         for(int i = 0; i < iterations; i++)
    25.             transform.position = Vector3.zero; //transform uses a Getter to retrieve the transform.
    26.  
    27.         stopWatch.Stop();
    28.         UnityEngine.Debug.Log(stopWatch.Elapsed);
    29.     }
    30.  
    31.     void TestMyTransform()
    32.     {
    33.         Stopwatch stopWatch = new Stopwatch();
    34.         stopWatch.Start();
    35.  
    36.         for(int i = 0; i < iterations; i++)
    37.             myTransform.position = Vector3.zero;
    38.  
    39.         stopWatch.Stop();
    40.         UnityEngine.Debug.Log(stopWatch.Elapsed);
    41.     }
    42.  
    43.     void TestMyTransformGetter()
    44.     {
    45.         Stopwatch stopWatch = new Stopwatch();
    46.         stopWatch.Start();
    47.  
    48.         for(int i = 0; i < iterations; i++)
    49.             myTransformGetter.position = Vector3.zero;
    50.  
    51.         stopWatch.Stop();
    52.         UnityEngine.Debug.Log(stopWatch.Elapsed);
    53.     }
    54. }

    This was all done in Unity 5.0.2f1
     
    Ziron999 and Korno like this.
  34. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    Thanks for sharing your insights with the community! However, I'd like to remind everybody reading this, that all these optimization tips are doing very little to nothing for the majority of projects. Beginners always tend to look for ways to "speed up" their code, where they really should be thinking about how to avoid bugs, bring their team together and release a working product. I'm looking through student's projects every year and the best working examples are those which are readable and easy to change in short time.

    I'd rather find the biggest problem in a project and then find a solution that is specific to the projects requirements. You're only wasting time caching every variable and trying to not do those "don't ever do this in Unity" things. In most projects, code is not even close to the bottleneck. You usually spend 80% on rendering. For code it only makes sense to look at very specific actions like "how do I load all my assets into the scene before my user is annoyed?", but then again, general tips need to be applied to those specific circumstances.

    As a suggestion to future posters who would like to help the community: Why not share your specific problem -> solution experience instead of general tips?
     
    Last edited: Sep 20, 2015
    MrDizzle26, garrido86, Ryiah and 3 others like this.
  35. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    That's why I keep referring to ExecuteEvents instead. It does the same job as SendMessage, but without strings and the associated problems. SendMessge should be pretty much considered deprecated in favour of ExecuteEvents. But the job they are set up to do is needed in a lot of places.

    I'm not sure how you could accomplish the same job as SendMessage using deal gates alone, I may be missing something in your thought pattern here.

    I'm not even going to bother. This approach flies against the face of everything about modular, reusable and maintainable code. It might make sense on a couple of extremely hot points. But you'll spend more time trying to code the system then you'll gain from using it. Read @Xarbrough's post for a more elegant phrasing of the same idea.
     
  36. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    http://stackoverflow.com/questions/16977694/c-sharp-why-is-dictionary-so-much-faster-than-list
    Finally found it! Here is the post that explains dictionary better then I can.

    Arrays are flat out faster for what you are doing. You wouldn't use list or dictionary. Try testing removing/adding/searching information instead because that's the only time you would use a list or dictionary (usually a dictionary). I see that you have added but never removed in that case array would be better. For searching dictionary would be better. However that does prove that arrays are fastest by a long shot on regular iterations so it's good to see the actually stopwatch math. I was just going by the profiler :)

    It is possible that lists might be faster at just adding and removing and that's it. This is something we are going to have to find out. This might be the only thing that will make a list good if you never ever plan on searching the data. However it would be a little strange to do a list for just adding and removing I guess you could use it as kind of like a queue with list[0] and then remove after it's done or something. Then there is no searching but hard to say if it's worth to even do that.
     
    Last edited: Sep 20, 2015
  37. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    Actually I did mention this already you just quoted 1 part. The part you didn't like...
    "Only use lists in cases where it is just 1 thing that needs to be handled that will be removed/added throughout the game but again putting a cache array limit is always going to be better. Even with bullets I do int maxBulletsAllowed; as the cache variable."

    It seems to me people are only reading part of what I'm saying?!
     
  38. sdviosx

    sdviosx

    Joined:
    Jan 4, 2015
    Posts:
    22
    Avoid SendMessage like the plague. I actually had a post regarding object to object communication via a delegate manager in which you suggested it can be replaced by ExecuteEvents or Unity Events which is a good alternative since you are referencing the event itself rather then the object as the target.

    Entitas seems to be a true ECS framework, decoupling, OTO communication and pooling is in its nature. Its worth a look. I am looking forward into using it on my next puzzle game.
     
  39. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    More like you're not reading what other people are saying. As I said, there's barely any difference in speed when comparing arrays vs. List for types such as GameObject, so you might as well use List and save yourself a lot of bother if you have an array that you're adding/removing from. This is pretty common in game programming. Dictionaries have their uses, but they're not interchangeable with List, since they're for quite different things. They have slower access speed, so you wouldn't use them unless you specifically need Dictionary functionality.

    --Eric
     
    Kiwasi likes this.
  40. MadeThisName

    MadeThisName

    Joined:
    Mar 14, 2015
    Posts:
    115
    This was defiantly a good read :)
    Just for laughs lets put this in the mix.
     
  41. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Slighty off topic, no offense, nor naming & shaming, but you've already tried to tell people on Unity Answers that their algorithms are worse solutions than your hard-coded hundreds-of-lines "if-else if"-construct which wasn't even finished, additionally accepted this statement as an answer and also accused others (who spend their free time helping and correcting people) of being a bit incompetent in regards of coding techniques (I'll just take your example with the nested loops).

    Long story short: It's time to come down a little.
     
    Kiwasi likes this.
  42. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    People know what you are saying, but these tiny performance gains are a drop in the bucket, and not worth the extra dev time and less readability they incur.

    It isn't about writing the fastest code, especially in the production environment. Its about writing code that is clean, easy and fast to write, that gets the job done with a acceptable amount of overhead.

    As a programmer i know what creates garbage and what is more efficient and less, but unless i know the piece of code i am writing needs to be ultra optimized, im not going to bother optimizing it, and will opt for tools like lists and linq where it saves me time.

    If performance and optimization was the only concern when it came to writing code, there would be no .net and c# since they are inherently slower than C or C++, and the same goes for most other modern languages. Or At-least within C# there would be no Collection types but array, and no linq.
     
    Last edited: Sep 20, 2015
  43. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    I did read it, I just didnt / still dont understand what it is you were trying to say there =)
    Hence the question "I am not sure as to what situation you were meaning when saying that" in regards to your whole statement. (I only quoted the main sentences of your statement though at the time. I was questioning whether to quote the whole thing or just the main of it.)

    I didnt read the whole page of the link you posted so I could be wrong, but from what I am getting from that page, its basically explaining how "Yes, if you have only unique "Keys" to represent a value, its better to use a dictionary than 2 lists (one acting as a "Key" the other as the value)"
    Welllll duuuuuuuuuhh XD
    The person was iterating through each list "Key" to find the value AND he was using lambdas / linq to do it (could probably be faster or slower depending on how he would have done it himself), while when he used the dictionary it was just the dictionary being used.

    If you are going to use a dictionary as a list, the list is probably better. If you are going to use a list as a dictionary, yes, the dictionary is probably better.

    Who knows, maybe using a dictionary as a list is better? Im not going to test it as thats probably such a micro optimization it would be a sin.
    Edit - Heck, if your only gana have unique values, then a hashset might be the way for you to go instead of a list or array.

    Here is how I use the 3.
    Arrays - Use when I am positive that all I would need is a fixed size / MUST have a fixed size. If the array might shrink or grow, list it is!
    Lists - Use pretty much all the time with some extension methods to make it faster (such as when removing an item, swap that item with the last item in list and then remove last item so the whole list doesnt "Move down 1"). The list also tells me that this collection does not have nor does it must have a fixed size.
    What does all your extra sized arrays tell you? Later down the road when someone reads your code and needs to add something to the array, but notices its an array with a hard limit put probably somewhere on top of the page and starts to be unsure as to if he cant increase the size. You'll have to start using comments to explain all the things you are doing (or a int variable name explaining it, your example maxBulletsAllowed would tell me to not go over that limit, even when in reality, it would have been fine to go over 5 or so).
    Dictionary - Use when you have unique keys that need to represent a value.

    I have my share of optimizations, mostly in regards to avoiding garbage collection (I would have disagreed with your "dont use foreach" statement if I wasnt also avoiding them due to the garbage foreach creates in unity ^^), but telling people to avoid lists, or worst, implying them to use a dictionary instead of a list, seems wrong.

    I am no pro at coding though, so who knows, maybe I am wrong =)
    One thing I do know, once unity 5 came out and the profiler was free, I have felt more relaxed with how I did my code. Before when I had no profiler, I would worry before writing my code about optimizations, now, I worry after. I think that is what the phrase "Premature optimization is the root of all evil" means =)

    P.S. - Sorry if this seems like I am attacking you. No anger at you here or anything, just disagreement with some things ^_^
    The problem we may be having here is in our heads we are taking what you say as a "General coding practice tips" type of way while in your head its a "Only, and I mean ooonllyy if you need every last drip of performance, do the following." I know you said at the end of the post that this is mainly for big games, but what is a small / big game?
    But in our defense, you did say "Avoid = don't use whenever possible..." which tells me its a general coding tip.
     
    Last edited: Sep 20, 2015
    Kiwasi likes this.
  44. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    282
    yes hiddenmonk the only difference is I'm saying "variable" instead of "Key" when I say a list is better with 1 object that needs to be constantly added/removed this is what I mean. Yes you can add and shrink a list but it takes time to do that. That's why you cache with an array, having an unlimited cache at your hands can only end badly and it IS slower then array's as proven. So for "speed" an array is better sorry but it is. The proof is right here with the stopwatch test on top of me physically seeing the difference myself in the profiler. So what if an array needs a limit, just make one? things shouldn't be unlimited...you don't have unlimited memory and speed do you?
    1. TestArray(); //result = 00:00:00.0025996 (~2.6 Milliseconds)
    2. TestList(); //result = 00:00:00.0089982 (~9 Milliseconds)
    3. TestDictionary(); //result = 00:00:00.0329073 (~33 Milliseconds)
    array 346% faster then list!
    array 1269% faster then dictionary!

    If you constantly need to add/remove then that means you will most likely need to search which means dictionaries are better because they are faster at searching. Why else are you building a list if you don't need information from it? If for whatever rare case that is not what you do then a list would be best.

    What's a small game? flappy bird, angry birds, a lot of phone games are small games. The amount of objects needed to be called/instantiated. It really just comes down to watching when your game starts going slow that's when these tips start to matter more and more.
     
    Last edited: Sep 21, 2015
  45. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,514
    How do we know what code is behind your 'TestList' and what not?

    If you just create a list, with out a size, then it's definitely going to be slower, because as you add values it's going to resize. A list really is just a wrapper around an array that's resized as needed. It can be reasonably fast if you set it up correctly... just like you have to do with arrays.

    In the end, yes an array will be faster though, as even a read from a list is going to require not just accessing the array inside, but also a call to the function. But if set up correctly up front, it's not going to be all that costly.

    At the end of the day, the difference between if you should use an array/list/dictionary comes down to what you're attempting to accomplish at that moment. One should know what each are, what they're used for, and what situations they're needed for. A dictionary is NOT a list and is NOT an array, they're used in way different manners.
     
    Kiwasi likes this.
  46. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    He was referencing the tests I did in a post above (post #33).
     
  47. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,514
    That's interesting, because those tests appear to all be accessing entries in the list or array (which honestly isn't a good comparison, as dictionary goes about accessing its entries in a different way than arrays and lists... again, different use cases).

    But yeah, I find it interesting because with a similar test I just wrote up and ran:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4. public class ListTestScript : MonoBehaviour {
    5.    const int SZ = 1000;
    6.   Foo[] arr = new Foo[SZ];
    7.   List<Foo> lst = new List<Foo>(SZ);
    8.    
    9.   void Start()
    10.   {
    11.   for(int i = 0; i < SZ; i++)
    12.   {
    13.   arr[i] = new Foo();
    14.   lst.Add(new Foo());
    15.   }
    16.   this.StartCoroutine (this.UpdateRoutine ());
    17.    
    18.   }
    19.   System.Collections.IEnumerator UpdateRoutine()
    20.   {
    21.   while (true) {
    22.   this.DoIterations();
    23.   yield return new WaitForSeconds(1f);
    24.   }
    25.   }
    26.    
    27.   void DoIterations()
    28.   {
    29.   const int CNT = 10000000;
    30.    
    31.   long t;
    32.    
    33.   t = System.DateTime.Now.Ticks;
    34.    
    35.   for(int i = 0; i < CNT; i++)
    36.   {
    37.   arr[29].value = 5;
    38.   }
    39.    
    40.   t = System.DateTime.Now.Ticks - t;
    41.   var dt1 = (double)t / (double)System.TimeSpan.TicksPerSecond;
    42.   Debug.Log(dt1.ToString());
    43.    
    44.    
    45.   t = System.DateTime.Now.Ticks;
    46.    
    47.   for (int i = 0; i < CNT; i++)
    48.   {
    49.   lst[29].value = 5;
    50.   }
    51.    
    52.   t = System.DateTime.Now.Ticks - t;
    53.   var dt2 = (double)t / (double)System.TimeSpan.TicksPerSecond;
    54.   Debug.Log(dt2.ToString());
    55.    
    56.   Debug.Log(dt1 / dt2);
    57.   }
    58.   class Foo
    59.   {
    60.    
    61.   public int value;
    62.   }
    63. }
    64.  
    65.  
    I actually get an average of 1 to 1 speed of accessing the lists to array. Sometimes the list being faster, sometimes the array being faster. Which I would expect... considering that really they're nearly the same thing, just the List having an extra 'method' call on top, which would have negligable effect on the speed. And the variance of one over the other just being general runtime speed variance.
     
    Last edited: Sep 21, 2015
    Korno and Kiwasi like this.
  48. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    (Variable SZ was missing from your code, so I made int SZ = 30)
    Weird, running your code I get these results.

    Array - 0.0156001
    List - 0.0936001
    Array / List - 0.166667556979106

    Which is similar to the results I got with my test. Why are we getting different results? My unity version is 5.0.2f1
    I read somewhere (links below) that datetime.now / stopwatch accuracy depends on your system. If you use my stopwatch example, do you get different results? What were your results anyways?
    http://stackoverflow.com/questions/2923283/stopwatch-vs-using-system-datetime-now-for-timing-events
    http://stackoverflow.com/questions/243351/environment-tickcount-vs-datetime-now
     
  49. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Hippo's performance tip: do less, time slice all the things. Really, so very much doesn't need to be done per frame.

    Most of the "optimisations" in this thread are meaningless, you're not iterating through thousands of objects. If a game is, it's probably going to benefit much more from time slicing.
     
    Suddoha, Kiwasi, Ryiah and 2 others like this.
  50. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,514
    This.

    Array, List, and Dictionary have their uses in their places.

    And sure, I'll run the test. I'm going to put it in a coroutine so I can run it repeatedly, and take the average. The first time a chunk of code runs can be misleading at times.

    It came out closer to what I expected last night than my test came up with (which could be tossed up to timing inaccuracies). The array was only slightly faster than the list on my computer, which I expect because it's just one extra method call. This is literally what the List does internally:
    (JetBrains decompiler)
    Code (csharp):
    1.  
    2. [__DynamicallyInvokable]
    3. public T this[int index]
    4. {
    5.   [__DynamicallyInvokable] get
    6.   {
    7.    if ((uint) index >= (uint) this._size)
    8.     ThrowHelper.ThrowArgumentOutOfRangeException();
    9.    return this._items[index];
    10.   }
    11.   [__DynamicallyInvokable] set
    12.   {
    13.    if ((uint) index >= (uint) this._size)
    14.     ThrowHelper.ThrowArgumentOutOfRangeException();
    15.    this._items[index] = value;
    16.    this._version = this._version + 1;
    17.   }
    18. }
    19.  
    20.  
    The index range test, and then return the item at that index. Not a lot of work there. If the overhead of this is what's slowing your game down... you have bigger problems.

    Running it at 100000000 iterations I got an average of 300 milliseconds for arrays, and 550 milliseconds for lists.

    And really... if I need a resizable list, I'm going to use it, because it gets the job done! Of course, I don't always need a list. So I sometimes use other things. I only ever use arrays if my collection is staticly sized.
     
    hippocoder likes this.