Zenject-2019: Zenject is too slow in large scenes

In the project we’re working at Lumentech, our scene has A LOT of components, before I started to use the Zenject, our scene loading was something like 20s, after we started to use it in this project it went to 120s, both times measured using an IPAD 3. I put a benchmark inside the SceneContext Awake and discovered the reason it was wasting so much more time, the zenject was trying to inject and resolve dependencies of EVERY COMPONENT IN THE SCENE.

Ok, I get it, it is a way to do, since this is a generic library you never know when somebody is trying to do an inheritance using transform and need an injection there, but the increase in time was unacceptable.

I solved the problem putting this naughty if inside the GetInjectableComponents of ContextUtil.cs

if (component is Transform || component is RectTransform || component is CanvasRenderer || component is ILayoutElement || component is TextMeshProUGUI || component is CanvasGroup || component is Button || component is ContentSizeFitter || component is Collider || component is IsoObject || component is Renderer) { continue; }

Turns out our scene had 17k transforms and 10k more others components and the zenject was trying to inject in every one of them. That list sums up the bigger amount of components that didn’t use injection but Zenject was trying to do anyway.

One solution to the problem is to create an ScriptableObject where developers can add types they want to ignore

Just to clarify, the benchmarks that I ran was using the real game that we’re working on, not a test scene, we really have that much things in the scene. The game is already in soft launch and runs quite well on medium to low devices.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 30

Commits related to this issue

Most upvoted comments

Also, another thing about instantiating prefabs using Zenject: it first instantiate the prefab and then it reparent If its necessary. Since unity 5.4 reparenting is very expensive, its a good Idea to instantiate directly as a child of the parent.

Sorry for any mispelling Im writing using my cellphone that is trying to autocorrect to another language(portuguese)

Ok I added what I think is the last remaining issue in this thread: build-time reflection baking. It is working in source (and documented) and will be included in version 6.5.0 and 7.3.0

I was investigating the Zenject performance from our perspective a bit. I tried a little micro benchmark with about 100 000 game object in the hierachy. The startup time of the scene without zenject was about 7s (“injecting” done in Awake()). When turning on Zenject, the startup time of the scene wen’t all the way to 85s.

It turned out that in this case the main culprit was LazyInstanceInjector.LazyInjectAll. I modified the method so that the objects are not removed from the HashSet one by one, but replacing the whole hashset at once. This took the scene startup time back down to 11s.

In our real world scene where there are close to 10 000 gameobjects (which starts in about 10s), the difference was, however, unnoticeable. So it did not help us, but it might help in some bigger scenes.

I’ve been investigating a way to potentially remove most, if not all reflection inside Zenject.

So far, tests are proving promising, but I need to test more, however, would be willing to talk about it in more detail, if you’re free and online.

Ok I committed a similar change to the LazyInjectAll method and noticed huge improvements with scenes with 100k+ transforms. In my test scene, that simple change to that method brought the startup time down from 80 seconds to 6. It seems that calling HashSet.First() can sometimes be a performance heavy operation. Thanks.