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
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Object pooling - Activating a game object is so slow it's not that much better than Instantiating

Discussion in 'Scripting' started by TwiiK, Feb 2, 2017.

  1. TwiiK

    TwiiK

    Joined:
    Oct 23, 2007
    Posts:
    1,729
    ... for me in my current project. (I couldn't fit that in the title)

    I've never really used any object pooling in Unity before because I've never needed to, but throughout my decade of using Unity I've always seen it heralded as the be all and end all of Unity performance optimization when dealing with a lot of objects like for examples bullets in a game and the consensus seemed to be that if you didn't use object pooling you were an idiot. And I still see people everywhere proclaim how stupid it is of Unity not to include object pooling with the engine because it is so essential. You know, that type of object pooling that fits every game...

    So now I'm actually working on a project where optimization is key. The whole premise of the project is to be able to handle 1000 units. So far it's working better than I expected. The built-in navigation system seems to be super optimized and handles 1000 units without breaking a sweat.

    But now I want my 1000 units to fire 1000 missiles at each other. So I proceed to instantiate 1000 missiles simultaneously and according to the profiler this takes 100ms. Then I remembered object pooling and plopped that in there. Now it takes 70ms to spawn 1000 missiles. I was expecting a much bigger performance improvement than that, and according to the profiler GameObject.Activate is the culprit.

    The pooling I implemented was based on Sebastian Lague's tutorial:


    But all pooling examples I've found so far use SetActive as their method for hiding/showing the objects.

    Sebastian is talking about a 60x speed increase in the beginning of the video whereas I don't even have a 1.5x speed increase. So obviously there's a lot more to object pooling than just putting it in there expecting massive gains, it needs to be tailored for your game.

    Obviously I'm getting a performance improvement in my game so I will pursue this, but so far I haven't seen anyone talk about the pit falls or gotchas with object pooling. A missile in my game so far is just a game object with a rigidbody, a collider and a trail renderer. The first things I will experiment with is just enabling/disabling individual components rather than the entire game object and having the missiles on the top level of the hierarchy rather than as children of the pool object.

    Not sure what I expect from this thread. I've googled some and seen that a lot of people already know that activating/deactivating a game object is slow, but it seems to depend on your game objects and your hierarchy. It would be fun for me to see some discussion around this or examples of alternatives if people have experience with this because like I said I haven't seen any pooling system with alternative approaches to just disabling/activating the pooled objects. If this has already been discussed to death then just let me know and I'll be on my way. :p
     
  2. Bantaru

    Bantaru

    Joined:
    May 11, 2016
    Posts:
    59
    Object pooling have one major advantage. Without it You spawn 1000 missiles and then You destroy all of them so garbage collector have to do it work to remove them from memory. Then again another batch of 1000 missiles is created and allocated into memory and the loop continues.

    With pooling You have Your 1000 missiles there allocated and ready to be used. So less processes are running at the same time and game do not need to constanty allocating and removing objects from memory.
     
  3. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    what about just relocating them, off screen on screen? or enable/disable renderer instead off setActive?
     
    image28 likes this.
  4. HelloMeow

    HelloMeow

    Joined:
    May 11, 2014
    Posts:
    279
    How are you profiling this? And are you logging anything?
     
    LeftyRighty likes this.
  5. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    Debug.logs are a full trace every time, so comment them out if you have them and try again :)

    you should be able to set the profiler to "deep profiling" and see specifically which bit is taking the time.

    I'd hold these as the suspects...

    I believe you can also do some optimisations with the physics by using a particle system... although I've not tried that myself
     
  6. TwiiK

    TwiiK

    Joined:
    Oct 23, 2007
    Posts:
    1,729
    That may be. I haven't really focused my attention towards garbage yet, but I'm getting a ton of it with the pooling as well. It's coming from things like GameObject.GetComponent and Component.get_transform. I will postpone that for later. It would be cool to see if I could manage it without garbage although this has never been a thing I have cared about, but then again I have never cared about optimization either because I've only ever made prototypes for me to enjoy on my fairly powerful computer. :p

    I'm not logging anything, I'm just using the profiler.

    GameObject.Activate is with deep profiling on and as low as I can get.

    I will try some simpler scenes for reproducing specific things. Without rigidbodies, with only bare game objects etc. I have a long list of things I want to experiment with.

    The biggest gotcha so far for me is just making sure everything I didn't need to bother with without pooling is actually happening correctly with pooling. Making sure the missiles behave like a new missile when they are spawned and don't carry over any of their old data and whatnot.

    And I just noticed that sound seems to require some special attention when dealing with 1000 instances. You don't play 1000 sound effects at the same time and get away with it. :p
     
  7. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    This. If you look around, there are a couple threads every year where somebody realizes SetActive is surprisingly slow. From what I've seen, it's faster to "manually" disable/enable things like isKinematic and the colliders and reposition the object off-screen. (That always surprises me since Unity is always talking about how they're working so hard to optimize poor performance with transform position changes.)

    Also, of course, you should be sure you're using an appropriate collection type.

    lol
     
  8. TwiiK

    TwiiK

    Joined:
    Oct 23, 2007
    Posts:
    1,729
    The cost of SetActive seems to be almost directly proportional to the number of components on the game object, although the cost of Instantiate is affected in pretty much the same way. Whether the pooled objects were children of another object or whether they were on the top level of the hierarchy didn't seem to make a difference. But I'm able to get over a 2x performance increase now compared to instantiating so there's no reason to turn back, closing in on 3x. I will stop comparing myself to instantiating now and rather just see what I can to do in terms of optimizing what I have.

    First of all, I will try what you suggest and just move the missiles off screen when they're "destroyed" and only disable/reset the bare minimum of components/variables needed to be able to fire them again.

    I will start a WIP thread for the project because this is so different from what I normally do in Unity and it's really fun to try and make a game on such a tight budget, and I'm guessing this could be of interest to others as well. I'll give away the project when I'm done with it or bored with it, it's usually the latter. :D

    Even something as innocent as having 1 Update() method per unit completely tanks the performance. It's seems like if you ever have to call something 1000 times it should be spread out over multiple frames and only happen once, anything that needs to happen per frame needs to be optimized similar to how the physics system or the navigation system is.
     
  9. TwiiK

    TwiiK

    Joined:
    Oct 23, 2007
    Posts:
    1,729
    That's actually smart. I could try using particles with collision for the missiles rather than rigidbodies. I did something similar before when I recreated Geometry Wars in Unity and used particles for everything.
     
  10. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,184
    As mentioned, SetActive has a certain overhead proportional to object complexity. Remember, that a lot of this work only needs to be done once. So if you we're comparing instantiating vs activating pooled objects the first time, you might not see a big speed improvement, but of course it's better memory management for the future and all subsequent call to SetActive will probably be much faster. This mostly applies to physics and custom awake calls, but maybe even more internal optimizations.

    I used this fact when optimizing my use of Spine animations. When the scene loaded I pooled all skeleton animations, but also activated them, waited one frame and put them back into the pool. This way, Spine had already cached all of its data loading from disk etc and wouldn't have to do it later when first using the objects.

    This might not always be possible and I'm not entirely sure if anything could happen, that removes this kind of cached data like garbage collection or optimization under high memory pressure, but in general it worked for me at least in the mentioned example.
     
  11. MV10

    MV10

    Joined:
    Nov 6, 2015
    Posts:
    1,889
    Just to reiterate what @Bantaru already mentioned, the primary driver to use pooling is normally to avoid GC, it isn't really about Instantiate performance, any benefits there are typically considered to be a nice bonus.

    I played with something that spit out thousands of projectiles and I gave each projectile a Time.time offset that I called "think"... If the Update happened before the think-time, the projectile was in a minimal-processing "cruise" mode. If the Update happened after think-time it would sort of wake up and do some extra processing, then add an offset to the current time for the next think frame. Worked reasonably well for my simple test.
     
  12. TwiiK

    TwiiK

    Joined:
    Oct 23, 2007
    Posts:
    1,729
    Thanks for the tips.

    I was able to reduce 1000 missiles down to just 5ms by using particles instead. They have the exact same flight and collision behavior as before so I'm not going back to rigidbodies. :p And with all the new particle improvements in Unity 5.5 I'm sure this will allow me to do some more cool things as well.

    But I will need to use object pooling for the units themselves and I will take everything you've said into account.

    Edit: It actually seems I spoke to soon regarding the particles. Actually colliding with 1000 objects with a particle system seems extremely expensive, as in way more expensive than anything else I've encountered so far in these experiments.
     
    Last edited: Feb 2, 2017
  13. image28

    image28

    Joined:
    Jul 17, 2013
    Posts:
    457
    Also Setting up MaterialProperties and using the new GPU Instancing will help greatly ( Only for gpu's that support it )
     
  14. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You are starting to get to the edge of what Unity can handle out of the box. 1000 units with a 1000 projectiles means you need to start doing tricks and using smoke and mirrors to make it happen.

    Switching the projectiles to a particle effect is a good first step. Note that you should only be using one particle effect for all 1000 projectiles.

    Then you want to kill projectile physics. Physics based shooters are for FPS games with a handful of players. For an RTS game you simply fake it.
     
    Ryiah, Suddoha, MV10 and 1 other person like this.
  15. radicalEd

    radicalEd

    Joined:
    Mar 19, 2016
    Posts:
    81
    If they're all roughly in the same area, most of missiles could be "holographic", not having the majority of the missile's properties and just mirroring the movement of their local "master"
     
  16. TwiiK

    TwiiK

    Joined:
    Oct 23, 2007
    Posts:
    1,729
    Thanks for all the replies.

    I've created a WIP thread now and released what I have so far for people to play with:
    https://forum.unity3d.com/threads/thousand-units-rts.454678/

    Yeah, I'm not doing any of that. :p The goal with this is to see if I can achieve everything without faking anything and I honestly can't stand games where things aren't simulated or consistent. I know these are things you probably will have to resort to at some point if you're selling games, but I've never had to sell a game yet so I can do whatever I want, my money comes from elsewhere. :D

    I actually tried both raytraced projectiles and particle projectiles, but in the end the physics system seemed to be the most optimized. Particle collisions were useless when you had to set it to 1000 collision shapes for it to be able to collide with all the units. At the moment I've just limited the number of simultaneous projectiles both for gameplay reasons and performance reasons.

    I feel like I've gotten what I wanted from this thread and I'll continue in my WIP thread. I guess I didn't really understand the primary benefit of object pooling until now, ref. what you guys have said about garbage collection. Thanks again for all the help.
     
    MV10 likes this.
  17. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    You must play a very tiny selection of games. All games are fakery and smoke and mirrors.

    The trick is to choose which parts make the most impact on the player and focus on getting those right.

    There is also no reason why a less detailed simulation can't be consistient with a more detailed one.
     
    MV10 likes this.
  18. TwiiK

    TwiiK

    Joined:
    Oct 23, 2007
    Posts:
    1,729
    You're right that I don't play many games, it's because I don't like most games and it's why I develop them instead, or at least prototypes that I think are fun. I can instantly tell the difference between a "gamey" game and a game that's based on consistent systems. And a gamey game has to be extremely well done for me to enjoy it.

    Gamey probably isn't a word or at least not one intended to be used in this context, but I mean games that are not based on systems, but rather where every feature or mechanic is implemented as a hack for a specific gameplay purpose, i.e. a trick. That is the game feels like a game and when you're playing it you're constantly reminded of that fact, i.e. you can blow up this wall, but not that wall, or you can get hurt by this rock, but this rock won't hurt enemies. Rock, paper, scissor game design in RTS games is an example of a very gamey mechanic, or projectiles that can't miss, or having a particle effect on a gun which leads you to believe the gun is firing a lot of bullets, but it's just a raycast with a damage over time effect, there are no individual projectiles.

    On the other hand you have a game like Spelunky where everything is consistent, everything is made up from interconnected systems, you learn the rules of the systems and then you can apply them wherever you wish. A game like that rewards experimentation while a gamey game punishes it and constantly has to remind you to only use whatever tricks the game has taught you so far and only apply them where you've been taught they can be applied.

    Total Annihilation was an RTS I really liked, and probably the only really simulated RTS that I've played, I don't count Supreme Commander because that game was just worse in every way. There was no rock, paper, scissor game design there, but only what you learned from playing. Obviously a slow moving turret designed to destroy buildings wasn't well suited to shooting down a fast moving plane, but it could shoot down the plane if it hit it, there was no bs "can't target flying units" or anything like that. :p

    This is actually the primary reason why I loved the original X-Com games, but think the new ones are trash. In the original games nearly everything was simulated, but in the new ones basically nothing is. And I'm working on a prototype for such a game myself where everything is simulated, a bullet passing through a wall can fracture and turn into multiple fragments which can hurt multiple enemies on the other side of the wall. I love programming things like that. This exercise is for me to see if it's possible to have a game with a ton of units and still have that level of detail and simulation.

    I know every game has to resort to a trick or two at some point, but hopefully you understand what I mean. If the projectiles in your game are fake then you will never have a situation where the projectile can bounce of a wall, hit an exploding barrel which launches a rock which in turn kills the target you intended to hit in the first place. And these types of games are very few and far between, either because they are so hard to make or because most people don't enjoy them, or really appreciate the details in them. Obviously I enjoy the other type of game once in a while as well, but they have to be extremely well done whereas I can enjoy a more simulated game even if it's a little rough around the edges because in such a game I can create my own fun.
     
  19. radicalEd

    radicalEd

    Joined:
    Mar 19, 2016
    Posts:
    81
    You're aware that Total Annihilation does, in fact, use several "fake" optimization tricks, right?

    When units are pathing, other units of the same type will follow the "breadcrumbs" left behind by them rather than pathfind on their own.

    "fake" optimization tricks are how you write actually good code.
     
  20. Aldo

    Aldo

    Joined:
    Aug 10, 2012
    Posts:
    173
    If you are doing this every time you enable the game object, yes, it's F***ing expensive. I imagine you use it to "Reset" the GameObject, what you should do is save the reference at Start/Awake and "OnEnable" call Reset(); that way you won't use expensive stuff like GetComponent
     
  21. Aldo

    Aldo

    Joined:
    Aug 10, 2012
    Posts:
    173
    What I mean about save reference is

    Code (CSharp):
    1. RigidBody rB;
    2.  
    3. void Start()
    4. {
    5. rB = GetComponent<Rigidbody>();
    6. }

    so at Reset() you cal

    Code (CSharp):
    1. void Reset()
    2. {
    3. rb.velocity = Vector3.zero;
    4. }
     
  22. radicalEd

    radicalEd

    Joined:
    Mar 19, 2016
    Posts:
    81
    This might be helpful to you; I've noticed that as long as you have enough RAM, an action performed repeatedly has some aspects of it cached.

    If you flicker all your missiles to enabled/disabled repeatedly at the start of the game, it might make it a little faster when you enable/disable them later.

    I would recommend finding a way to make them "disappear" without touching enabled/disabled, or any physics attributes, however.
     
  23. TwiiK

    TwiiK

    Joined:
    Oct 23, 2007
    Posts:
    1,729
    As long as it's not noticeable by the player I don't care. :) I'm talking about the games where if you hit someone with the edge of your shotgun blast it deals the exact same damage as hitting them point blank because the shotgun is just a cone trace rather than individual pellets, or things like that, which in my opinion you find in nearly every game ever made.

    There's a big difference between "engine optimization" and "gameplay optimization" in my opinion. What you give here as an example could already be done in the navigation system built-in to Unity and neither I nor the player would notice.

    Thanks, I have a lot of things I want to try, this was just my first stab at this.