BrandGhost

Be LAZY - The Fast Path To App Startup Time in DotNet

You've been building up your application over time, and trying to use things like Autofac (or other dependency injection frameworks) to get your code organized. You've found that your startup time is getting slower and slower... What can we do about this?! For more software engineering videos, check this out: https://www.youtube.com/watch?v=9NIhzWDAmzE&list=PLzATctVhnsghjyegbPZOCpnctSPr_dOaF Have you subscribed to my weekly newsletter yet? A 5 minute read every weekend, right to your inbox, so...
View Transcript
all right so you've been building programs and you found over time that as you're adding more and more features the startup time of your program is getting ridiculously slow it used to be something that would start up super fast you can start interacting with it right away maybe it's a service maybe it has a user interface and now all of a sudden you find that you're waiting 5 10 or more seconds before you can actually interact and use your program from my own experience as I've been building programs using Auto fact to use dependency injection if you haven't seen any of those videos you can look up here and I'll link them above but if you've been building applications with auto fact like I have you might find that as your applications are growing bigger and bigger that there's more stuff that's loading all at the start of your program and now you're sitting there wondering okay I've been following all these practices to use dependency injection that's the right thing to do why is it that I've organized my code in this way that everyone says is the right way and all of a sudden my program is super slow to start up that can can't be right well there's a technique that we can apply and that's lazy loading so you probably heard about lazy loading in many different contexts but today we're going to be looking at something built into C sharp that can help with lazy initialization and if you're not familiar with that this is the lazy class that is built right in it is lazy with a tight parameter T and using this built-in functionality we can decide what we want to lazily initialize at a later point in time so that it's not all right at the start of our application and we're going to talk about some of the trade-offs that you're going to want to consider when you do this because lazy initialization doesn't necessarily just make the problem go away it just moves it to a different point so if that sounds interesting stick around and let's jump right over to visual studio alright so I'm here in visual studio and I just have a sample application pulled together that we can start to look through and see how lazy might fit into this so this is definitely contrived but I just wanted to have something really simple to demonstrate how this might work so at the start of our application I just have a couple of console write lines and if we look at lines one through four we can see that I have this kind of laid out to be where our application might be initializing and doing some startup work and I want you to just be able to kind of think about this example and maybe relate it to your own code where you're doing a bunch of you know class creation maybe you have to go load some files from disk so you can have some stuff pulled into memory and you're doing this all at the start of your program so in our case we're going to be calling this do once method to go load some magic number we'll look at that in just a second and then lines six through nine which are right here this is going to be where we're kind of just simulating what an application might be doing again very contrived in our case here but we're just going to use that magic number and you'll notice that I'm going to be printing out the date and time for when these things are occurring just so that we have a frame of reference and we can try to talk through what lazy is going to do in this case so let's jump down to the do once method and the do work method we'll start here with do once and you'll see that I'm just saying we're going to simulate some long-running operation so let's pretend that we have to go you know parse some files from disk to go load in some configuration and pull out some magic number that we want and it's going to be one two three of course I'm just making all of this up right now so I just want you to be able to think about how this might work and instead of me coding up something I just want to simulate it with the thread sleep this 2000 is going to sleep for two seconds if we look at the do work method it's going to look very similar we're just taking in that magic number we're going to sleep for three seconds instead and if we imagine that this method's actually doing some work when really it's just sleeping all we're going to do is print out that magic number so the two of these methods are just trying to demonstrate that we have work being done at the very beginning of the program and then some work that's actually what the program mostly entails so if you can try to relate this to a program that you've written where you have a lot of slowdown that's occurring in the initialization of your app and let's see how we can try to move some things around using lazy when we look at the output from running this program we can see that we have the starting and then started console right line calls and they are two seconds apart as we had in our code when we see the starting work and then the actual magic number getting printed out here is one two three and then finished work we have a delay of three seconds which is also what we expected from our code so the point of this output is really to help us get a framing for what an application startup might look like so in this example between starting and started with that two second delay that's really just simulating that we have two seconds of startup work to take place and then this portion here that's taking three seconds is the simulated application running that's going to take our magic number that we needed at initialization time that's value is one two three so the thing that we're trying to address here is that this portion here takes two seconds at startup so we have a two second delay before we're actually doing any work now in this example like I said it's a little bit contrived but if you can imagine that this portion here between starting and started the larger that our application gets and the more initialization that we're doing at the very start of the program we're having this delay that might increase from two seconds to five seconds and Beyond and depending on what your application's like this might be time that you want to minimize before you actually start doing some work and just to give you some examples if you have a web service that's starting up you actually might want to be able to start serving web requests very quickly and if you have a lot of initialization you're actually going to delay when you can start actually serving traffic if you have a graphical user interface program so say you're working with WPF or Maui and trying to create some applications that the user can interact with of course if they have a really long startup time that could be quite frustrating think of the last time you tried to play a video game and if it took a long time to start up you'd be pretty frustrated if you had to wait for that before you could even play so in this example we're not going to be looking at optimizing this time in fact we're actually saying that we're willing to have this time increase as long as we could have this time decrease so let's go see how we can do that all right so with this contrived example I mean one of the most obvious things we could do is quite literally just copy this down to here and voila now we've now moved all of our initialization logic to be later well instead of doing that and cheating because that may not work for your application you may actually want to be initializing things at the start of your application but not necessarily doing any heavy lifting to go load up values or do any processing so this is where we can introduce lazy to work around this to change up this example to use lazy all that I'm going to do is actually wrap this code here on line three with lazy what I'm going to do is replace the VAR here with actually just having lazy int because it takes a tight parameter of integer and I'm just trying to do this to be a little bit more explicit and then we can do new lazy end and actually just call this method like this to be slightly more verbose you could use an anonymous delegate so that you have your syntax like this but in this particular case it's just a little bit redundant to have to do this you can see or it might be hard to see but visual Studios actually suggested that we can just trim that right out and have it cleaned up but you'll notice now on line eight it's not happy and it's not going to compile because we have this red squiggly line under magic number and that's because magic number is no longer just an INT It's a lazy end and the way that we can access the value of that is simply by putting value so before I go running this code let's just review what we've changed this code up here is supposed to Mark the starting and started portion of our application to indicate when we're actually doing our initialization and how much work that actually is in terms of time now that we've wrapped this in lazy what we would expect to see is that starting and started happen at almost the exact same time effectively it should feel instantaneous and we should see that when we have this printed out and that's because lazy when we instantiate it isn't actually going to do this do once work quite yet instead when we look between lines 7 to 9 when we call do work this is the first time here on line eight when we're accessing the value property of the lazy class that we have so instead what I would expect we see is that because this is the first time we call Dot value we should incur the penalty just once to be able to get this one two three value coming back so that means we're going to have a sleep of two seconds right here and then of course when we call do work we're actually going to have one more sleep of three seconds so what I would expect to see if I scroll back up is that starting and started happen at almost the exact same time and then starting work and finish work happen about five seconds apart let's go see if that's what happens all right if we have a look at our output here we can see that starting and started both occur at this time here and we can't actually see the milliseconds and everything printed out based on how we did the console right line but we can assume that it's almost effectively the exact same time when we look at the starting work and then finished work we can see that we go from 1201 to 1206 so that's actually a five second wait this means that what we've effectively done is moved all of our initialization code the penalty for it to actually be incurred during this starting work and finished work portion of the application so as I said at the beginning of this we're not actually resolving any time that it takes we're actually just moving where we're going to incur that penalty now another property of lazy is that it's only done once and to prove that let's go ahead and actually just write magic number dot value to the console several times to prove that every time we ask for DOT value on magic number that we're not actually paying this two second thread sleep penalty I've just copied and pasted this line here four times and it will print out the magic number is and we should expect this to be one two three but what we should notice is that when it took five seconds to actually go run this code before without the console right lines in between it should still take five seconds now that I've put these in here and that's because magic number dot value is lazily initialized and we're only paying that penalty once right here all right and as expected when we look at the starting work line which starts at 15 25 we can actually see that five seconds later we get finished work and we do have this magic number is one two three printed out five times here these are the four that I added manually so this is proof that it took no extra penalty to go ask for the magic number value after we initialized it the first time all right so that was just a quick video on how the lazy class Works inside of C sharp and just to recap on what we were able to do was actually take some code that might take some time at your initialization point in your application and the frame of reference that I wanted to give you is if you're doing things like plug-in loading or other Auto fact type things where you just have a lot of stuff going on in the very beginning of your application you might find that over time as your application is getting bigger and bigger that amount of time to initialize is getting longer and longer so instead of trying to initialize everything for your application all at the start instead we can try to find other opportunities to offload that processing to something more just in time for when you need that value the example that I walked through today was of course a little bit contrived it's kind of silly but I wanted to kind of demonstrate to you different portions in a program so you can relate that to a program that you might have by wrapping things in the lazy class you're actually able to defer when that value is going to be initialized and if you have to pay a penalty to initialize that value in our example I was just using a thread.sleep but hopefully you don't have something like that in yours maybe it's loading a file or doing something else like that that penalty that you would incur at initialization time is instead incurred at a later point in time probably just before you need to go use it or whenever you're calling the dot value property of your lazy instance so now you've seen how that works when my next video is ready I'll link it right up here and we'll look at how you can do lazy async initialization of values in your code thanks for watching and we'll see you next time thank you

Frequently Asked Questions

What is lazy loading and how does it help with application startup time?

Lazy loading is a technique that allows us to defer the initialization of certain resources until they are actually needed. This means that instead of loading everything at the start of the application, we can load resources just in time, which significantly reduces the startup time.

How do I implement lazy loading in my C# application?

To implement lazy loading in C#, you can use the built-in Lazy<T> class. By wrapping your resource initialization in a Lazy<T> instance, the resource won't be created until you access its value for the first time, which can help improve your application's startup performance.

Are there any trade-offs to consider when using lazy loading?

Yes, while lazy loading can improve startup time, it doesn't eliminate the initialization cost; it just postpones it. This means that if your application has a lot of deferred initialization, you might experience delays later when those resources are first accessed.

These FAQs were generated by AI from the video transcript.
An error has occurred. This application may no longer respond until reloaded. Reload