Last night I was reviewing ASP.NET Web API Source Code that I noticed this snippet:
private static Func<object> NewTypeInstance(Type type)
{
return Expression.Lambda<Func<object>>(Expression.New(type)).Compile();
}
But surely, compiling a lambda expression is really costly (as we have seen in the last post), why shouldn't we use simply do this (if we are suppose to just return a Func):
private static Func<object> NewTypeInstance(Type type)
{
var localType = type; // create a local copy to prevent adverse effects of closure
Func<object> func = (() => Activator.CreateInstance(localType)); // curry the localType
return func;
}
Well, I asked this very question from Henrik F Nielsen, ASP.NET Web API team's architect. And he was very helpful getting back to me quickly that "compiling an expression is the fastest way to create an instance. Activator.CreateInstance is very slow".
OK, I did not know that, but it seems to be a good topic for a blog post! So here we are where I compare these few scenarios:
- Direct use of the constructor
- Using Activator.CreateInstance
- Using a previously bound reflected ConstructorInfo (see previous post for more info)
- Compiling a lambda expression every time and running it
- Caching a compiled lambda expression and running it (what ASP.NET Web API does)
Test and code
In this one, I could get a bit more imaginative with my code since in the last post I already established overhead/merits of various code invocation methods. For the object to construct, I use a simple class which has a default parameterless constructor. Results will be different using a parameterful constructor but I think parameterless constructor is a more pure case.
So I have created a few Action extension methods to perform the tedious repeated snippets in the last post. Each method runs 1,000,000 times which is not high enough but as we will see (and have seen in the last post), compiling a lambda expression every time is really slow so 10 million would be very high.
public class ConstructorComparison
{
static void Main()
{
const int TotalCount = 1000 * 1000; // 1 million
Stopwatch stopwatch = new Stopwatch();
Type type = typeof(ConstructorComparison);
var constructorInfo = type.GetConstructors()[0];
var compiled = Expression.Lambda<Func<object>>(Expression.New(type)).Compile();
Action usingConstructor =
() => new ConstructorComparison();
Action usingActivator =
() => Activator.CreateInstance(type);
Action usingReflection =
() => constructorInfo.Invoke(new object[0]);
Action usingExpressionCompilingEverytime =
() => Expression.Lambda<Func<object>>(Expression.New(type))
.Compile();
Action usingCachedCompiledExpression =
() => compiled();
Action<string> performanceOutput =
(message) => Console.WriteLine(message);
Thread.Sleep(1000);
Console.WriteLine("Warming up ....");
Thread.Sleep(1000);
Console.WriteLine("Constructor");
usingConstructor
.Repeat(TotalCount)
.OutputPerformance(stopwatch, performanceOutput)();
Console.WriteLine("Activator");
usingActivator
.Repeat(TotalCount)
.OutputPerformance(stopwatch, performanceOutput)();
Console.WriteLine("Reflection");
usingReflection
.Repeat(TotalCount)
.OutputPerformance(stopwatch, performanceOutput)();
Console.WriteLine("Compiling expression everytime");
usingExpressionCompilingEverytime
.Repeat(TotalCount)
.OutputPerformance(stopwatch, performanceOutput)();
Console.WriteLine("Using cached compiled expression");
usingCachedCompiledExpression
.Repeat(TotalCount)
.OutputPerformance(stopwatch, performanceOutput)();
Console.Read();
}
}
public static class ActionExtensions
{
public static Action Wrap(this Action action, Action pre, Action post)
{
return () =>
{
pre();
action();
post();
};
}
public static Action OutputPerformance(this Action action, Stopwatch stopwatch, Action<string> output)
{
return action.Wrap(
() => stopwatch.Start(),
() =>
{
stopwatch.Stop();
output(stopwatch.Elapsed.ToString());
stopwatch.Reset();
}
);
}
public static Action Repeat(this Action action, int times)
{
return () => Enumerable.Range(1, times).ToList()
.ForEach(x => action());
}
}
Results and conclusion
Here is output from the program:
Warming up .... Constructor 00:00:00.0815479 Activator 00:00:00.1732489 Reflection 00:00:00.4263699 Compiling expression everytime 00:02:11.5762143 Using cached compiled expression 00:00:00.0855387
- Using a cached compiled expression is almost as fast as the constructor
- Using Activator is x2 slower
- Using reflection is x5 slower
- Compiling a lambda expression every time is really slow: in this case + x1000 times slower
Activator.Createinstance < T >() creates an instantiated object of T. But your compiled lambda returns Func< object >(). Should'nt you call Invoke on that func to get a precise result?
ReplyDeleteI am. Note the () at the end of all lines such as ".OutputPerformance(stopwatch, performanceOutput)()".
ReplyDeleteGeneric Activator.Createinstance has no place since (Activator.Createinstance<T>) since if you know the T then you might as well call the constructor.