Skip to main content

Speeding up dependency injection

ActiveJ Specializer optimization

ActiveJ Specializer is a library that optimizes code for JVM. You can simply combine it with ActiveJ Inject and speed up your code by up to 30% In order to set up ActiveJ Specializer, simply use Injector.useSpecializer() before Injector instantiation. ActiveJ Inject compiles bindings in runtime into a highly efficient representation. In conjunction with ActiveJ Specializer the bindings can be turned into bytecode that will be as efficient as a manually written code. In this way you get the best performance in real-life projects without maintaining hard to support manual code.

How it works

To understand how ActiveJ Specializer works you can read specializer documentation. Here we will use a simple example to try to explain how specializer works in regards of ActiveJ Inject.

A specializer transforms class instances into a dedicated classes where instance methods are replaced by static methods and instance fields are replaced with static fields. Instances that are specialized are actually instances of CompiledBinding.

Let's say we have a module that defines a following binding:

@ProvidesInteger length(String string) {    return string.length();}

A compiled bindings here may look like this (a simplified version):

public class CompiledBindingImpl<R, U> implements CompiledBinding<R> {    private final CompiledBinding<U> otherBinding;
    private R instance;
    public CompiledBindingImpl(CompiledBinding<U> otherBinding) {        this.otherBinding = otherBinding;    }
    public R getInstance() {        if (instance != null) {            return instance;        } else {            U otherInstance = otherBinding.getInstance();            this.instance = createInstance(otherInstance);            return this.instance;        }    }
    protected R createInstance(U otherInstance) {        // ...    }}

If we use Injector.useSpecializer() before creating an injector we would get a specialized compiled binding (a simplified version):

public class CompiledBindingInteger implements CompiledBinding<Integer> {    private static Integer instance;
    public Integer getInstance() {        return getInstanceSpecialized();    }
    public static Integer getInstanceSpecialized() {        if (instance != null) {            return instance;        } else {            String stringInstance = CompiledBindingString.getInstanceSpecialized();            this.instance = createInstance(stringInstance);            return this.instance;        }    }
    public static Integer createInstance(String otherInstance) {        // ...    }}

Since specializer replaced instance methods with static methods and instance fields with static fields this allows for additional JVM optimizations as well as neglects an overhead of dynamic dispatch. Also, it may seem as a minimal optimization, imagine that you have a huge dependency graph and you need to create instances in a runtime. For example when using ActiveJ Inject for HTTP request handling when you need to instantiate new objects upon every HTTP request. In this scenario a performance boost of using a specializer would be significant.

RPC request handling speed up

We have created an example of using ActiveJ Inject + ActiveJ Specializer to boost a performance of RPC server that uses dependency injection for RPC request processing. To learn more about ActiveJ RPC you can visit this page.

A dependency graph on a server is similar to the one in Cookbook example. Each incoming RPC request is handled within RPC scope.

The example also comes with a dedicated becnhmark tool that you can run to measure the performance on the server.

Try to run server and benchmark took as-is and measure the performance. Then, uncomment a Injector.useSpecializer() line in ScopedRpcServerExample and re-run server and benchmark tool once again. You should see a significant performance boost compared to not using ActiveJ Specializer. We have measured up to 2 times performance increase on the server.