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.
It would make sense if Activator.CreateInstance() would take parameters just as the non-generic version does (if you're inside some generic class/method and the type you want to create does not have a parameterless ctor)… but alas, the gods at Microsoft have not bestowed that upon us :| which is really a shame IMHO. Just as there not being any parameterful 'new' constraint.
DeleteThanks for your experiment, and also for the idea with the compiled expression, that should allow for the creation of a generic factory class where the output type needs parameters for construction, yet still be quick to instantiate!
PS: I know, I know, it's been a long time, but this blog post is still the first useful thing that pops up when you google C# instantiation performance comparisons ;)
Thank you Johann for your kind and warm comments. It is good to know this post still can benefit some in the websphere :)
DeleteIt definitely can!
DeleteAs an added remark: Just profiled the expression based factory against direct instantiation; the class I instantiate is a WCF duplex client which takes an InstanceContext of its client implementation as its constructor argument.
Since the duplex client class does some initialization the first time it's instantiated, I ran my tests externally, and once with direct instantiation first, factory second, and once the other way around, for a hundred times each (and each of the 100 times with the app restarted via a script, to make sure it does all its initialization every time).
When running the factory instantiation first, it averaged
15.32ms for factory instantiation
00.18ms for direct construction (87.5% vs. factory when running direct AFTER factory)
When running the direct construction first, the averages were
00.215ms for factory instantiation
15.22ms for direct construction (99.3% vs factory when running direct BEFORE factory)
This is with the compiled lambda being generated and cached once for the factory and type, and not included in the measurements here (because it will be insignificant in comparison with the total runtime, in any case, it averaged well below 0.2ms here)
The factory looks like follows: https://dotnetfiddle.net/SZRvPT (although I moved the instance context construction out of the factory's construction call for profiling). Unfortunately dotnetfiddle doesn't know System.ServiceModel… :P
Hope this helps someone, too!