34

In ASP.NET Core Dependency Injection, I just wonder if registering Singleton instances will improve performance instead of registering Transient instances or not?

In my thinking, for Singleton instance, it just costs once time for creating new object and dependent objects. For Transient instance, this cost will be repeated for each service request. So Singleton seems to be better. But how much performance do we gain when using Singleton over Transient? Thank you in advance!

DanielV
  • 2,076
  • 2
  • 40
  • 61
tuq
  • 1,328
  • 2
  • 11
  • 21
  • 3
    I think it's not the case of better or worse and yes a case of the right one, that depends a lot when talking about dependency injection – João Paulo Amorim Feb 20 '19 at 16:01
  • 2
    This is not a matter of performance. You should use the solution that you need. – Silvermind Feb 20 '19 at 16:02
  • 2
    I agree with @Silvermind's sentiment, however, you may prefer prefer to use `Singleton` instead of `Transient` in situations where the observable behavior isn't impacted to reduce the overhead of object construction and graph traversal. However, like all optimizations, you should measure to see whether this matters at all. – Matthew Feb 20 '19 at 18:22
  • @JoãoPauloAmorim, thank you for the reply. In my case, both Singleton or Transient is OK for using, so I consider to choose the better performance. Singleton can cause leaking of memory, but I think it is better performance. – tuq Feb 21 '19 at 01:55
  • @Silvermind, thank you. Did you measure the performance of them or is there any link refer to this? – tuq Feb 21 '19 at 01:55
  • @Matthew, thank you. You meant Singleton will be better performance? As you said, I should measure to see. – tuq Feb 21 '19 at 01:55
  • It can be, in scenarios where you have very deep dependency graphs or scenarios where the initialization of the dependencies are expensive, at the expense of memory. – Matthew Feb 21 '19 at 13:24

3 Answers3

65

Like others have said, performance should not make your decision here: performance will not be dramatically impacted either way. What should be your consideration is dependencies, both managed and unmanaged. Singletons are best when you're utilizing limited resources, like sockets and connections. If you end up having to create a new socket every time the service is injected (transient), then you'll quickly run out of sockets and then performance really will be impacted.

Transient scope is better when resource usage is temporary and of minimal impact. If you're only doing computation, for instance, that can be transient scoped because you're not exhausting anything by having multiple copies.

You also want to use singleton scope when state matters. If something needs to persist past one particular operation, then transient won't work, because you'll have no state, because it will essentially start over each time it's injected. For example, if you were trying to coordinate a concurrent queue, using semaphores for locks, then you'd definitely want a singleton scoped service. If state doesn't matter, then transient is probably the better scope.

Finally, you must look at other services your service has a dependency on. If you need access to scoped services (such as things that are request-scoped), then a singleton is a bad fit. While you can possibly use a service-locator pattern to access the scoped services, that's a faux pas, and not recommended. Basically, if your service uses anything but other singleton services, it should likely be scoped or transient instead.

Long and short, use a transient scope unless you have a good, explicit reason to make it a singleton. That would be reasons like mentioned above: maintaining state, utilizing limited resources efficiently, etc. If the service will work in a transient scope, and there's no good reason to do otherwise, use transient scope.

Now, ASP.NET Core's DI has both a "transient" and a "scoped" lifetime. Both of these are "transient" in the sense that they come and go, but "scoped" is instantiated once per "scope" (usually a request), whereas "transient" is always instantiated every time it is injected. Here, you should use "scoped" unless you have a good, explicit reason to use "transient".

Pang
  • 9,564
  • 146
  • 81
  • 122
Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • 1
    +1 for your explanation. However, I kind of confused with the last statement on transient vs scoped per transaction. Microsoft states that "Transient lifetime services are created each time they're requested from the service container. This lifetime works best for lightweight, stateless services."That means for REST api it is recommended to use transient? – codebased Jun 03 '19 at 11:24
  • It's talking about the service you're injecting, not the "service" as in an entire API. My point was that you should pretty much just always use `AddScoped` unless you have a specific use case that actually warrants using the other methods like `AddSingleton` or `AddTransient` to register your services. – Chris Pratt Jun 03 '19 at 11:44
  • 3
    Great answer! But let me note, that there is a class of services where the simple rule "Scoped is best if no other reasons exist" may mislead you. This is especially true for .NET Core's own services. For example you would think that AddScoped is perfect for the HttpContextAccessor service. But it is not. You should use `services.AddSingleton();` in this case. Bottomline: Check the docs (or StackOverflow) for the correct lifetime of any services that you did not write yourself! – Jpsy Nov 20 '19 at 08:07
  • 1
    In my case, using transient instead of Singleton, causes an overhead of at least 200ms. And I don't have a single constructor that does anything else that field assignment. But there are a handful of classes that need to be created. Since its a chatbot service, every ms is visible on the user. There are cases, where a call becomes 5x slower. So there are cases, where performance matters. – Konstantine Aug 22 '20 at 18:05
  • Does the DI system instantiate the controller at each request or is the controller singleton? – variable Jan 12 '22 at 18:10
  • @Konstantine a 200ms overhead does not sound right to me. I really think there must be something really wrong going on with your services if that's the difference you are observing on transient vs singleton. – julealgon Feb 18 '22 at 18:08
3

I know that the performance here is not what you should looking for, but for a matter of curiosity, I made a sample benchmark app to evaluate the difference between AddSingleton and AddTransient, using BenchmarkDotNet

The solution that I used can be found here (Feel free to improve it): https://github.com/iheb719/BenchmarkDifferentServicesInstances

This was my summary :

My laptop specifications : BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19042.1415 (20H2/October2020Update) Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores .NET SDK=6.0.101 [Host] : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT DefaultJob : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT

My results :

Method Mean Error StdDev
CallSingletonApp 101.0 us 0.64 us 0.53 us
CallTransientApp 108.3 us 1.16 us 1.03 us

So there is really no significant difference between AddSingleton and AddTransient (of course, if you don't have huge treatments inside your constructors)

ihebiheb
  • 3,673
  • 3
  • 46
  • 55
1

Like mentioned in the replies this isn't really a matter over performance.

For more details please see the link below where the difference is very elaborately explained. AddTransient, AddScoped and AddSingleton Services Differences

The only way that this will matter performance wise is if your constructor is doing a lot of stuff. This can and should be avoided though in all cases.

DanielV
  • 2,076
  • 2
  • 40
  • 61
swforlife
  • 528
  • 3
  • 16
  • thank you for the reply. In my case, I consider performance and leaking memory when using Singleton over Transient. Of course, both of them can be applied in my case. – tuq Feb 21 '19 at 01:58
  • 1
    Well like mentioned there, and in other comments here it is not just up to performance. The performance gain or difference within your application will be massively dependent on the further context of your application. The only way to really verify this is to actually run your scenario and test it and even then,,,,, There are probably plenty of other things that will give you a more significant performance improvent. – swforlife Feb 21 '19 at 15:44