Dependency Injection Performance Issues

In my profiler reports, I increasingly see the results of prototyping using dependency injection. Many of the dependencies were static, but since we want to test methods in isolation, they change to instance members, as in the following example:

class ShortLivedThing { IDependency1 dep1; IDependency1 dep2; IDependency1 dep3; ... int TheRealData; // Constructor used in production public ShortLivedThing() { dep1 = new Dep1(); dep2 = new Dep2(); dep3 = new Dep3(); } // DI for testing public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) { dep1 = d1(); dep2 = d2(); dep3 = d3(); } } 

In turn, dependencies most of the time have other dependencies, and so on. This results in an instance of the tree (mostly "static") of objects each time the method is called outside of the tests. Each of the objects is very small (just a few pointers), but the effect of the tree turns this into an ever-increasing hit of performance.

What can we do about it?

+7
dependency-injection
source share
6 answers

It seems to me that you need to use the functions that a suitable dependency injection infrastructure can give you. Do not use other constructive logic for testing / production.

When using spring, single injections are only performed when the container starts. Prototype injections are performed every time. Full wiring is also performed every time you run unit test, if connected. Thus, profiling tests are usually not a good idea.

Perhaps you are using too few single core areas and too much prototype? (Prototype = new instance every time)

The attractive thing about spring injection is that you can use proxy objects, i.e. your object graph might look like this:

  A Singleton | B Singleton | C Prototype (per-invocation) | D Singleton | E Session scope (web app) | F Singleton 

And each request will create only one instance of C and one instance of E per session. A, B, D and F are single. If this is not a webapp, you do not have a default session area, but you can also create your own areas (the Window area sounds cool for a windowed desktop). The key here is that you can “enter” areas at any level, effectively you can have ten layers of singleton objects, and suddenly something session appears. (It can really revolutionize how you implement some cross-cutting features in layered architecture, but that's a different story)

This really gives the smallest possible object creation inside the DI model. I think.

Although this is spring for Java, I believe a number of other DI frameworks should support similar features. Perhaps not the most minimalist.

+8
source share

I think you should only have a "DI constructor". You call this constructor for testing, as well as during production.

 class ShortLivedThing { IDependency1 dep1; IDependency1 dep2; IDependency1 dep3; ... int TheRealData; public ShortLivedThing(IDependency1 d1, IDependency2 d2, IDependency3 d3) { dep1 = d1; dep2 = d2; dep3 = d3; } } 

Thus, you have no problem copying the object tree every time a method call is made outside of your tests. Of course, for production, you need to properly connect your objects outside the participating objects themselves, which is good.

In short: don't switch to 50% hard coding DI / 50%, go to 100% CI.

+2
source share

How about passing links?

+1
source share

If you're worried about slow tests, try running them in parallel and don't let the testing process interrupt your programmers.

Automation of this process:

  • When someone checks, build from the repository.
  • Run the tests of this assembly.
  • E - Send the results to the developer who registered.

It is better if the first check is not done for the real repository. Make it temporary and build it. If you wish, you can perform performance tests, style checks, etc. And include them in the email. If you do this, add one step to the automated process:

  • If the tests pass (and additional criteria are met), merge the new code with the real repository.

Thus, slow tests do not bother. Also, when a developer needs to know that her code has broken something or made the productivity increase that she expected, she simply checks and waits for the email generated for her.

+1
source share

The best I can think of is to include all the dependencies in one “contextual” object, which is then shared among all instances. This should somewhat alleviate the performance problem.

0
source share

If you are targeting .NET, check out Autofac . It has various applications (singleton, factory, container) for setting up aspects of creation, deterministic deletion to keep resource usage free and allows using GeneratedFactories and lambda expressions to set up components and avoid reflection costs.

0
source share

All Articles