Search Unity

Discussion Creating my own DI - I'm stuck!

Discussion in 'Scripting' started by funvr_12, Nov 10, 2022.

  1. funvr_12

    funvr_12

    Joined:
    Apr 5, 2022
    Posts:
    3
    Hi Guys,

    Trying to create my own IoC Container in Unity. Finding it quite difficult to make a performant, lightweight injector for Unity. I'd appreciate your experience for anyone who knows it has experience in this area.

    Essentially my problem is I've created custom attributes to decorate private fields with
    [Inject]
    attributes, and I want to set these at runtime (
    Awake()
    ). The thing is basic reflection like checking attributes and class types are pretty light performance wise, but getting and setting values via reflection is much more costly. I've tried some various ways round this:

    1. Creating Expression Trees and compiling the code at runtime. It's fast ONCE it's compiled, but their initial compilation is EXXXXPPPESSNIVE. After that it's very performant, but I only need to access each field once. So this is a no-go.

    2. Looking into Source Generators to create methods at compile time that can act as accessors and can be invoked at runtime. There's problems with this, like the invocation cost would need to be done via reflection, again, an expensive operation, and then all related classes would need to be
    partial
    .

    Has anyone successfully created a lightweight automatic IoC Container in Unity that doesn't use reflection for getter/setters? I've made a pretty one with reflection, but with several thousand fields in the Editor and when playing it straight up died.

    Thanks!
     
    Last edited: Nov 11, 2022
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Does it have to be done at runtime? Could it not be done in something like OnValidate in the editor?
     
    funvr_12 likes this.
  3. Nad_B

    Nad_B

    Joined:
    Aug 1, 2021
    Posts:
    730
    Well, although it's known that Reflection is slow (and especially MemberInfo.Get/SetValue), I don't think it's that slow, unless you're calling them tens/hundreds of thousands times each frame.

    Do you have any numbers/benchmarks to show how slow they are?

    I actually did a benchmark and here are the results:
    upload_2022-11-12_12-15-36.png

    The test was to assign 3 properties, a string, an int, and an object (class) directly or via Reflection (PropertyInfo.SetValue).

    At first look, Reflection performance seems abysmal compared to directly setting the values (around 45 times slower!)... but can that really cause a performance problem in a game?

    The answer is: it shouldn't, since 360 ns for setting 3 properties = 0.0001 ms per property.

    So unless, as I said above, you are assigning tens of thousands of properties via Reflection each frame (which you shouldn't do), using Reflection to set values shouldn't cause any problem at all.

    I think this is a case of premature/useless optimization. As @Kurt-Dekker always says: never assume, always profile.

    PS: I've created my DI framework, and I just use Reflection SetValue (with FieldInfo caching) without any problem, in thousands of GameObjects (not created at the same time of course)

    _____________________________

    Benchmark code: https://gist.github.com/nadjibus/a5700410dab368695342105063a27703
     
    Last edited: Nov 12, 2022
    funvr_12 and Kurt-Dekker like this.
  4. funvr_12

    funvr_12

    Joined:
    Apr 5, 2022
    Posts:
    3
    Well, I did a stress test of 500k fields in a scene and calling without reflection (ugly pattern)
    I have something like that for some actions, but for a IoC Container runtime will be required to resolve instanced services.
     
  5. funvr_12

    funvr_12

    Joined:
    Apr 5, 2022
    Posts:
    3
    There will be up to or above 1000 decorated fields that will require various getter/setters for not-null validation and injection. So I guess the main concern is scalability. I'm in work right now, so I'll make benchmarks tomorrow. Just posting now to show this isn't becoming a necro thread :).
     
    Nad_B likes this.
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Keep in mind this is EXTREMELY fraught with lifecycle edge cases:

    Your code is not the application. Unity is the application. Your code is just a minor guest at the party:

    https://forum.unity.com/threads/res...-problem-when-using-ioc.1283879/#post-8140583

    I suggest you avoid reaching for anything you might have used in traditional C# development because it is almost certainly going to fail spectacularly and mysteriously when applied in Unity.

    Perhaps a ScriptableObject with all the requisite information may be a solution for you? We use that extensively for configuring every part of our games.
     
  7. Max-om

    Max-om

    Joined:
    Aug 9, 2017
    Posts:
    499
    We have used this method for invoking remote calls in our multiplayer game. Its fast enough even with that old mono runtime they are still using.

    https://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker

    Though it is used to invoke methods not set properties. Though I think it could be nicer to mimic constructor injection anyway? To bad you cant use readonly keyword on the injected services though :/

    edit: GetMethodInvoker is slow though so needs to be called at scene start
    edit: or if you iterate over all types in your assembly and prewarm the cache