Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/Compilation #412

Merged
merged 3 commits into from
May 8, 2018
Merged

Feature/Compilation #412

merged 3 commits into from
May 8, 2018

Conversation

seesharper
Copy link
Owner

This PR adds support for compiling delegates up front.

Initial docs

Compilation

LightInject uses dynamic code compilation either in the form of System.Reflection.Emit or compiled expression trees. When a service is requested from the container, the code needed for creating the service instance is generated and compiled and a delegate for that code is stored for lookup later on so that we only compile it once. These delegates are stored in an AVL tree that ensures maximal performance when looking up a delegate for a given service type. If fact, looking up these delegates is what sets the top performing containers apart. Most high performance container emits approximately the same code, but the approach to storing these delegates may differ.

LightInject provides lock-free service lookup meaning that no locks are involved for getting a service instance after its initial generation and compilation. The only time LightInject actually creates a lock is when generating the code for a given service. That does however mean a potential lock contention problem when many concurrent requests asks for services for the first time.

LightInject deals with this potential problem by providing an API for compilation typically used when an application starts.

The following example shows how to compile all registered services.

container.Compile();

One thing to be aware of is that not all services are backed by its own delegate.

Consider the following service:

public class Foo
{
	public Foo(Bar bar)
    {
    	Bar = bar;
    }
} 

Registered and resolved like this:

container.Register<Foo>();
container.Register<Bar>();
var foo = container.GetInstance<Foo>();

In this case we only create a delegate for resolving Foo since that is the only service that is directly requested from the container. The code for creating the Bar instance is embedded inside the code for creating the Foo instance and hence there is only one delegate created.

We call Foo a root service since it is directly requested from the container.

In fact lets just have a look at the IL generated for creating the Foo instance.

newobj Void .ctor() // Bar
newobj Void .ctor(LightInject.SampleLibrary.IBar) //Foo

What happens here is that a new instance of Bar is created and pushed onto the stack and then we create the Foo instance. This is the code that the delegate for Foo points to.

The reason for such a relatively detailed explanation is to illustrate that we don't always create a delegate for a given service and by simply doing a container.Compile() we might create a lot of delegates that is never actually executed. Probably no big deal as long as we don't have tens of thousands of services, but just something to be aware of.

LightInject does not attempt to identify root services as that would be very difficult for various reasons.

We can instead use a predicate when compiling services up front.

container.Compile(sr => sr.ServiceType == typeof(Foo));

Open Generics

LightInject cannot compile open generic services since the actual generic arguments are not known at "compile" time.

We can however specify the generic arguments like this:

container.Compile<Foo<int>>()

LightInject will create a log entry every time a new delegate is created so that information can be used to identify root services that could be compiled up front. In addition to this, a log entry (warning) is also created when trying to compile an open generic service up front.

@seesharper seesharper self-assigned this May 8, 2018
@seesharper seesharper merged commit 54b4096 into master May 8, 2018
@seesharper seesharper deleted the feature/warmup branch May 8, 2018 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant