Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

AddComponent's horrible performance.

Discussion in 'Scripting' started by Vince-Chimp, Aug 13, 2018.

  1. Vince-Chimp

    Vince-Chimp

    Joined:
    May 28, 2018
    Posts:
    42
    So, i have a project where i have no way around using AddComponent about 28000 times on initialize (mainly because it is an external tool, and i might be able to spread it later, but that is besides the point)

    I have noticed that this takes several MB's worth of GC, and 2800MS to do so. (Unity 2017.3.1p4)

    So i did some research and it all comes down to this one or 2 bug reports i found with little other info:
    https://issuetracker.unity3d.com/issues/addcomponent-high-gc-usage

    https://fogbugz.unity3d.com/default.asp?746364_pjnmdhk7c9imgdsk

    https://feedback.unity3d.com/suggestions/calling-destroy-on-a-component-is-very-expensive

    So what the hell is going on here? Why is my component adding so slow? I call it often, but that is about it. It is just a monobehaviour, with no start/awake behavior, which has 1 public class which is a data class that contains 4 whole bytes and is serializable. I don't see why the hell this issue exists.


    Can anyone enlighten me as to why this happens or what good it is for, and any way around this problem in general?
     
    Last edited: Aug 14, 2018
  2. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I have a hard time believing you really have no way around using AddComponent 28000 times. It is simply not intended to be used like that.

    Also your last link is to a performance test of GetComponent, not AddComponent - important difference.
     
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You really need to restructure if you are running with 28000 components in a single scene. Its not going to go down well. Even if you get the AddComponent section right, there are still a thousand other problems with that many components that are just waiting to bite.

    I would strongly recommend handling your objects in vanilla C# instead. Perhaps with an ECS type structure, although there are other structures that work just as well.
     
  4. Vince-Chimp

    Vince-Chimp

    Joined:
    May 28, 2018
    Posts:
    42
    Ah, in my rage i missed that bit. Makes sense.


    Well, obviously there are like 10 better ways to do this, but i don't get the time to implement my own way to render voxels so i am stuck with this dumb plugin that likes to instantiate a metric ton of cubes apparently. The massive garbage collection still doesn't make sense to me either way.
     
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    My guess is since AddComponent is intended to be used sparingly, that Unity consider the per call GC to be acceptable.
     
  6. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    If you bring rage to a game development battle you will always without fail, lose. Just like a boxer that brings rage to the ring - angry boxers also always lose - badly.

    It's a purely cerebral exercise and if you find yourself getting angry with anything in development, you need to take a step back and plan alternative approaches. There is no other way.
     
  7. NeverConvex

    NeverConvex

    Joined:
    Jun 26, 2013
    Posts:
    88
    Sounds like the voxel plugin isn't well-suited for your purposes. Would probably make more sense to discuss how it currently works, how to fix it for your use-case, and how you're using it rather than how to improve AddComponent performance.
     
    Joe-Censored likes this.
  8. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    I wouldn't consider it too bad for all the stuff that has to be done by AddComponent:

    - gathering type information (if not cached) for attributes and various initialization steps
    - transfering all of the required information that to the C++ side (if that's even the correct order - perhaps it's a back and forth, who knows?)
    - allocation and construction of the native component object
    - internal initialization of the component, including some of the information gathered in previous steps
    - allocation and construction of your actual C# component
    - injecting deserialized information into the C# object and the C++ object

    Plus a lot more, I guess.

    All of that in a non-optimal one-by-one approach. The new ECS does exactly address these problems, and reduces the overhead. It seems though, that it doesn't help you if you're dealing with external plugins.

    That's also quite some overhead, if you're doing that on start-up, count in some engine initialization steps, scene loading, deserializing all the other information etc.

    If you do not have the option to reduce the number of components, as suggested, try to spread the overhead using coroutines. And, if the plugin does not allow it, it's either not well documented how to use it efficiently yet effectively, or it is being used in a wrong way, or it's simply not designed for this task. Or perhaps it's simply a bad one.
     
    hippocoder, Doug_B and Joe-Censored like this.
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Its just going to break again later... Don't say I didn't warn you.
     
  10. patrik-org

    patrik-org

    Joined:
    Sep 10, 2012
    Posts:
    100
    I think this is a big issue. I suspect AddComponent is called or same code when instantiating prefabs (as adding an empty game object + components has about the same performance as instantiating it as a prefab), now 28 000 AddComponent calls might seem extreme, but if each game object has an average of 4 components that is 7000 objects - not really that many.

    Since we are moving more towards object composition (through nested prefabs, etc), you want these operations to be fast. And object pooling does nothing to help memory usage, so it's not always an option.

    I still think there is something wonky going on. Nothing seems to be cached, as it takes similar amount of time each time. If it relies on reflection, caching type information is probably a good idea.

    I have a framework that does a lot of reflection, XML parsing, dictionary lookups, etc. when instantiating game objects - yet AddComponent takes about 50% of the object creation time... there must be room for performance improvements, and it's a method used every time you want to create game objects during run-time.
     
  11. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    ECS/DOTS is the solution. There's no point in trying to massage monobehaviour which is basically the equivalent of a family car with a tiny petrol tank in a race vs 1000 top end racing motorbikes.

    So even if it is wonky, someone can likely apply the same lazy code or wonky code to ECS and it will just be super fast, so should they try so hard or invest same resources into just using DOTS?

    I suppose I'm trying to say right tool for the job...
     
  12. patrik-org

    patrik-org

    Joined:
    Sep 10, 2012
    Posts:
    100
    I admit I haven't delved that deep into ECS and DOTS, but I have a hard time seeing it as a silver bullet. I'm not terribly excited by the way the Unity component system works (in fact I'm working around it a lot), but the entire unity GUI framework is built on it and it's far from obvious to me how ECS and DOTS would be a better fit for UI systems in general - specially if you leverage well-established UI design patterns such as MVVM, data binding, declarative design languages, separation of concerns and a few other things such as asynchronous on-demand loading of UI elements, dependency properties, etc. The performance is actually great and the developer can focus on designing their objects and building upon existing objects.

    So I'd say those things are the right tools for the job in my case.
     
    NotaNaN, koirat and phobos2077 like this.
  13. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,010
    Damn it is slow.
    I have a dynamic kind of prefab that when instantiating adds components to itself.

    Ex: It traverse the hierarchy and adds some components to all transforms with colliders. So I don't have to mess around with components every time I change my prefab structure.

    Unfortunately when my prefabs are created I got noticeable hiccups in FPS.

    I deep profile. And I see hierarchy of AddComponent chain:

    GameObject.AddComponent total - 16% self 0%
    GameObject.Internal_AddComponentWithType total - 16% self 5.5%
    GameObject.AddComponent total - 11% self 10.8%

    All this for only 120 components. (self ms 26.05)


    btw. Can I somehow skip Internal_AddComponentWithType ?
     
  14. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Did you somehow miss the six posts above yours mentioning DOTS as the way to solve this problem? DOTS is purpose built to solve this exact type of problem.

    Not if you want the component to be actually added.
     
  15. PaulR

    PaulR

    Joined:
    Nov 14, 2012
    Posts:
    43
    Not sure what this component is doing or what your code looks like, but maybe; just maybe, you can try a SOA (structure of arrays) approach.
    In other words, instead of 28000 objects with 1 component on each; you can try 1 component which contains arrays of length 28000 (28k positions, 28k colours etc...)

    You’d need to restructure the code quite a bit, but could work.
     
  16. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,010
    I did investigate DOTS sometime ago I was unable to find solution for my problem.
    I'm doing raycast against colliders, my current way of doing things is to get transform from RaycastHit and search for particular components on it (those components are added when I instantiate prefab).
    How DOTS can help me with this problem.

    Well it is invoking some kind of GameObject.AddComponent again, so I thought it might be possible to invoke this second one directly.