Is awaiting a Task instead of returning it directly in C# actually slower?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everybody i'm link in this video i want to address one of the questions that come around all the time when the discussion goes to uh whether you should return async task or task and whether the state machine generated is actually slow and whether the memory locate is actually significant and we're gonna take a look at those questions in this video and try to address them and answer them and i'm gonna give you my own opinion on this whole matter but basically this is the example we're gonna be looking at you have a very simple public task method that returns a task and you have the same method but in this time you have async await should you do that or not so first things first what is the big concern that anybody has so the concern that people have is that async weight will actually when the code is lowered it will generate a state machine and that state machine is what gives a single weight its power to do asynchronous coding that looks like synchronous coding the problem is that that involves allocation obviously the state machine and potential performance concerns because you have to go through the states so why not you just omit it and have it like this and these codes will effectively function the same they will eventually be awaited something down the pipeline will await them but here you are not awaiting so this should be faster right and maybe it should even allocate less memory so let's take a look at that i have here a benchmark that i already created with benchmark.net if you're not familiar with benchmark.net i have a video on that you can click on the top right corner of your screen right now and check it out and what i'm doing is i have the exact same method here and then i have a task that returned the string and the reason why i'm doing that is because i want to see what's the actual performance degradation without having to worry about the task itself like if i do an i o call you won't see a difference i can guarantee that and i actually will prove that later with a significantly smaller example so what we're trying to see here is what will the actual state machine cost us so i have two examples two benchmarks here which are calling the with and the without async methods and i'm going to run them now and i'm also using a memory diagnoser for this which means we're going to get memory allocations for those executions as well and we can talk about them when the results are back so results are back and as you can see the guest ring with a weight async is more than double the nanoseconds slower remember though nanoseconds one nanosecond is one thousandth of a microsecond then one microsecond is one thousandth of a millisecond so in order for those to make a difference on the millisecond level you need one million of them and one billion to make a difference in the second level and you'll see why this isn't significant at all but for now let's talk about the one that might be significant and that is the memory located it's allocating double the bytes here and that's because of the code that's being generated behind the scenes and in fact if i could just copy that and paste it in shoplab.io which you can check out on your own as well it will show you the lord code generate behind the scenes and if i just increase that a bit um you probably can see it but if i go to the very bottom in the one that doesn't use away they think here you'll see that it's just returning the task the code is not really lowered to anything else as opposed to the one with the weight is saying that is generating the state machine then it creates the all the method builder starts with the state starts the state machine and then returns the task and you can see all the code that the state machine will generate here so obviously significantly more code we are using extract but it still is slower because you have to go through the states so yes there is no question about it that this code is actually slower in the context of nanoseconds and it is actually more expensive memory wise so based on those examples you would say that yeah that seals it you don't want to use a single weight right well let's create a new file here async http client example and we're gonna use this code over here and let's take a look at the code before we run anything with this we have an http client and then we have two methods one that will try to get a string from um by the github api to try to get my profile as a string from the github api and this will try to get it again but with async await now the github api will reject those requests because i'm not using a user agent header so an exception will be thrown when i call them let's take a look at the program.cs i will just change this to debug and comment this out and what i want to do is i want to create that a class so example equals async http client example i'm gonna use a new here and i'm gonna say try not true try here we go and i'm gonna remove the throw and first i'm gonna oh i have to change this to asynctask of course and first i'm going to say await example dot get string with async so this one is with async await and the other one will be without and remember this will throw an exception so i'm going to run this and hopefully i'm going to print the exception in the description in the debug box and yes that's true and actually let me just create a new line between them because you can't really see what's going on there so if i run this again you should be able to see exactly the two exceptions remember first one has a single weight even though it's one line the other one doesn't and you can actually see that the exceptions are quite different so what is the difference well the difference is that if we go at the code that we called uh without and if i search here you can't actually find it you don't get a notification you get an exception for the thing that actually failed if i do the other one it's there this is for me the only reason why you should use async await whenever you can because it will wrap the exception and the thing that through in the actual task that through it now what you see here is this get string async core in the one that doesn't have the async await do you know where that is that is within the http client method so it's not here it's not here it's not it's here so that's where you actually gonna get the exception and as you can see you're not getting this because they are not using a single weight there but that's deliberate but they are using it here and this is where you're gonna get your exception and for me that's the key like 20 nanoseconds per async await that you didn't actually have a single waiting you didn't generate stick machine it's not worth it like even if you want to make the point for performance it's still not worth it because you're gonna save that time when it comes down to debugging and solving this problem in the future so you really have to weigh the pros and cons and for me saving even one minute of debugging over one minute of processing yeah i mean at the end of the day you can choose whichever way you want to go with but for me i'd rather have it so it makes my debugging and my obviously the logging is better you can see exactly what went wrong here you don't know what went wrong you don't know even the line that this actually blew up you get the async client line like here you don't even get the line that your own code through you just have to assume that it's somewhere in the main and then look at the main and see what happened there so honestly i my advice would be use it it makes the code easier to work with as well and if you don't want to trust me by telling you to use it david fowler software architect in microsoft an absolute genius is also recommending that you should use it now just to make the original point stronger and just to show you why it doesn't actually make a difference i'm going to create a json example here for the benchmarks and we're going to paste some code which i'm going to um comment and let's take a quick look through that code so we have an anonymous object here with a bunch of data and we're gonna try to serialize it through a memory stream which also can be awaitable and as you can see here i have two alternatives i'm awaiting and serializing it or i'm just sorry i'm not a weight against realizing it or i'm just a single waiting and serializing it and it goes into the memory stream and then we serialize the memory stream to an array of bytes and then the array of bytes to a string or you can find all this code if you want description down below and we have a boolean here saying use a sing method yes or no and we have again two benchmarks here so one of them will use the one that doesn't have a weighted sync and one of them will use the one that does have a way to sync and this should be slower right yes so let's just add the memory diagnoser as well in here and run those benchmarks so i'm gonna go to program.cs i'm gonna comment all that out and i'm gonna uncomment this change this to avoid and run this benchmark change this to result release sorry and let's see now what something that does more work than just looking at the state machine very isolatedly in an isolated way and what the performance looks like on that level so results are back and let's see what we have so the memory difference is not visible because other things load level it and overshadow it greatly so one point goes away and as you can see the one with a single weight is actually faster than the one without why did that happen well it's actually not faster it's just all within marginal error because all of those nanoseconds that you're worried that you're gonna lose are so insignificant when other things are being added into the mix that you shouldn't even worry about it so like i've said before and like david father has said before you use async away it will make your debugging better your diagnostics better your logging better and you won't actually lose anything that will make a difference in return so that's my opinion on the matter that's all i have for you for this video thank you very much for watching special thanks my patrons for making videos possible if you want to support me as well you're going to find a link in the description down below leave a like if you like this video subscribe for more content like this ring the bell as well and i'll see you in the next video keep coding
Info
Channel: Nick Chapsas
Views: 26,874
Rating: 4.9530687 out of 5
Keywords: Elfocrash, elfo, coding, asp.net, .netcore, dot net, core, C#, how to code, tutorial, asp.net core, csharp, lesson, microsoft, microsoft mvp, .net core, nick chapsas, chapsas, asp.net core 3, .net core for beginners, await async, await async best practices, await, async, await async mistakes, asynchronous, async Task, Task, C# Task, C# async, Is awaiting a Task instead or returning it directly in C# actually slower?, GetAwaiter, continuewith, dotnet, .net
Id: Q2zDatDVnO0
Channel Id: undefined
Length: 11min 23sec (683 seconds)
Published: Mon Mar 29 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.