So You Think You Know C#? For vs Foreach

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up YouTube welcome to this next episode on so you think you don't see sharp in today's episode we're gonna be looking at a for-loop versus a for each loop now the for loop is just gonna be a small little portion of this believe the focus is gonna be on the for each loop and how it works and what happens under the hood and how you can make your own classes that you can use within a for each without them being collections of some sort maybe all know that we can use any collection with that for each loop but I want to show you there's some other tricks up the see chef compiler essentially the duck typing if you saw my previous episode I sort of hinted at it so we're gonna see some of that occurring in the c-sharp compiler is called duck typing is called structural typing it's called pattern matching it's got different names to it but I'm gonna stick to duck typing hmm alright so the first thing we're going to look at is the performance difference between a 4 and a 4 h2 right and then we're gonna have to try and explain what we see or what our findings are to get us better sense of okay so this one's faster than other but like what's going on why is it faster or slower to try and explain or justify the reasoning behind the differences in the performance so I've got a very simple application here that I'm benchmarking and I've got one benchmark method that is using a for each loop with a predefined collection so the timing does not include the creation of the actual collection itself it's just it's just includes the iteration through the collection and then my baseline is one the one with the for loop same thing is solely the collections pre initialized it's not part of the timing alright so if you would look at the test results to benchmark is also this annular on this is going to take a few minutes and now be back alright so the benchmarking is done there are a few things that I wanted to sort of pay attention to one because we set up the the for loop as the baseline in the ratios column here you'll see that the for loop is 1 and then because this is 2 it implies that this is twice as slow as the baseline which is a for loop so the for each loop is about twice as long or the for loop is about twice as fast depending how we want to see it the other difference is that the for each is allocating memory while the 44 loop is not so and that's number reason for why it's slower I'm gonna be digging into what that memory allocation is why the four is just slower and what's really going on under the hood all right so so much for the the benchmark now when you're trying to understand what the what happens at compile time after compilation the benchmarking is happening with the code execution and in release build and so it implies that the JIT compiler has already sort of kicked in and the JIT compiler may have done this own optimizations that I at the time of JIT compiling for example most languages have a optimization for for each for for this what's called loop unwinding or unrolling of the loop and it works kind of like this if at compile time you can decide or determine the exact number of iterations within a loop the DAR then having a loop actually specified in the generated final generated code it has unwound unroll the loop and so if you're going in the loop on a time this can actually write the line of code hundred times like with the different changes that occur different compilers have different capability with the gosset optimization but I want to just keep in mind that moving forward we're gonna be looking at il hand compiled C sharp chord but not JIT compiled the JIT compilation step happens only at run time so the benchmarking sit is also a showing kind of what you would expect to get in real world applications what we are seeing in IL is the non JIT compiler code so just keep that in mind there are a few tools that you can use to decompile il and so ones like build the application here and there's an X Z I can then use various tools to kind of reverse engineer that il back to c-sharp now for what we are trying to do we don't actually we would like to have a tool that takes the aisle and produces the raus raus c-sharp code mean this is c-sharp version to corral and c-sharp others version 7 or 8 tools because the language features are adding that magic and we want to see kind of the raw version of that c-sharp naughty the seashell version that has the magic in it if that makes sense and there are different tools this aisle spy which is a free tool you can download from the marketplace from the visual studio marketplace and install into Visual Studio and you'll see me using that there's a beautiful website called sharp lab sharp lab dot IO where you can copy/paste some code and as long as it can compile it it will show you a decompile version of that or the C sharp lowered version of it it's unfortunately also very smart as a result of which is not showing you the real lowered code but will try and make use of a couple of tools like this to get the better sense and eventually we can also just go down to aisle and see if we can decipher from the by looking at the aisle what's actually occurring in C sharp at the C sharp level past the compliation stuff there so this video is trying to be trying to explain why the for each is slower than the for loop and maybe from all of this you might say you know me when I can use a for loop maybe I should either full up and I'm gonna leave that up to you I would suggest only in cases where the slowness has shown to cause a problem if you're building some framework level code that is in a hot spot meaning lots of times being called in a thousand thousands of times and there has been demonstrated the issues performance issues switch over to the for each for loop and see and that speeds it up of course memory allocations also take place with the for each loop so maybe you want to be smart about that in hot paths and so typically only in hot paths should be looking for some sort of optimization at this level all right so okay so I'm going to take all of this code and I'm copying what a copy pasted into a sharp lab dot IO and there it is and what you're going to see here is that this is the code I paste it in so our original code here and then this is the code that has been produced after let's say a compilation step so you can see here that the for each benchmark method has been converted to something a little more than the original which is over here I mean this is what we wrote and this is what it became the battery should be to take away that a for each loop does a couple things most times it does a couple of things one it uses the getenumerator method in other words whatever this thing is that you wanna eat it over has to have a method called get a numerator for it to start working and then it puts in a try finally because the numerator typically is an i disposable and so it's gonna try and dispositive it's an i disposable because of the way the guidance is behind implementing an enumerator is that it needs to make copies of the original data so that in case you're multi-threading or whatever the copies cannot affect each other while iteration is going on so it's thread-safe so it depends on the implementation really but that's a guidance and as a result you might want it to be a disposable so once you're done with that iteration you can dispose of it and it's not holding on to unnecessary memory allocations inside the try finally we have a while loop and this is how it works with any enumerator so when you have a get in numerator the numerator itself has these three methods one is current the method called move next and another method called reset which is hardly ever use so you'll see rope over here the move next returns a bowl and so if it can move next he'll continue on and then once it continues on the current property will give you the current item and then you can use that within the loop so your code that he wrote like this is the for each gets converted to code that is like this the for this is one way it gets sort of Lord if you will in c-sharp but your voyage becomes a wild over the try finally but it's it little smarter than that I see is quite a bit smaller in line what if you were using an array instead of an eye in Newell this will be doing here so if I were to now make this I say here did two array you'll see that the same for each has now changed to using a simplistic forage but this is not really a simplistic forage this is where I said the tools don't tell you the full story they don't tell you the correct story if you will they're trying to be smart on the decompile the core meaning from Eyal back to c-sharp based on language features and everything else they're kind of they're trying to get back to the language feature that we have today or the language version that we have today and the features that that language version has which is not what we want we wanted to show us the raw code really what's happening here is when it works with an array or a list rather than producing a using the getenumerator method and the try finally and a while loop is going to use a for loop right so the for each loop can either be lured to a try finally with a while loop inside it and the getenumerator method being used which is a expensive method as in there's an allocation taking place right there or it could be converted to a regular for loop because it knows it's working with an array or a list all right so that's a optimization so there's two different things that are going on at the time of loading with for each depending on what's happening what is the Getti numerator now this guidance behind how to implement it of course we have a feature in c-sharp since 3 v version 3 v called the iterator pattern that is implemented using the you'll return and it makes it very very simple for us to implement and i anĂ­bal without having to go through a lot of lot of work right so if you want to know how has to be done I've made a video many many years ago I think in the c-sharp so you think you can see sharra so you think you can link for 7-8 years ago and i forget with this a part 1 or part 2 thing is probably part 2 where i talk about implementing our own enumerator and seeing what it takes to do that and that's kind of like what's happening yes any time you see four double here in the compiled version of the source code that it says it's doing a get enumerator like that is not a free method this is a lot of work this creates a news of another object each time you do a forage and then that object as we gob is collected as well as the fact that it's doing that work and it's a natural while do with the try finally with the dispose all that adds to so the performance problems if you will not problems the slower performance as compared to a for loop right have this getenumerator method here is also pretty cool one would think that the sketching numerator is prolly because you know there are innumerable interfaces ienumerator interfaces and you have to implement in your classes you have to have the same class implement the ienumerator and the annual interface for it to work here in a hazard for each so I'm going to take this method here I'm just gonna write this here and back in my code [Music] we have to make this too right and if I got this getenumerator here you'll see that's the Ainu Bowl interface has a method called get enumerator and the getenumerator method or interface is a whole bunch of other methods that move next and the current and everything else I was talking about so that's all showing up over here and this is a numerator of T so if you look at that here we are so every enumerator has a current property a move next method and a reset method but for the most part in dotnet c-sharp at least nothing actually uses the reset method on a new error but if you're implementing the interface you need to implement that maybe it's doing nothing maybe throw an exception that's up to you most things don't actually call it alright so then one will think that if you wanted your own thing that he wanted to for each or that you would have to implement the enumerator interface that dick was the ienumerable interface and so on so of all that is true to some extent as and you have to do that I mean you don't have to that if you did that it'll still work with the foliage right so what I have here is a movies class in a movie class that's my movie class it's gonna collapse this that's a singular movie and I also have a movie collection class meaning is called movies it doesn't descend from a collection it does not implement an eye numerator or ienumerable or any other star is just a standard poco class right that actually doesn't implement any other interface or dissension of nothing else I want this class to work with the foliage now given the name you can assume that it is got a collection in this case movies so it's it's a start descending from a collection is not think i aggregating a collection per se but I wanted to work with the foliage so what I want to do is something like this I would like to be reduce ample now say movies yielded new movies and that the plural if you will the collection and then I'd like to for each war movie in movies neither this one that's what I want to do now you can see already there it's actually working as there's no the compiles not complaining because I already done the implementation and this is what the duck typing comes into play in c-sharp the one and only key sign off and I'll call that much I'm not sure that they call it duck typing I think they call it structural typing or pattern matching all delayed something like that doesn't matter what it is all I'm saying is one would think that this forage would probably expect you to have a class that implements the IMO numerator and I in herbal interfaces and that of course makes sense because that actually works it's not actually require all it takes for you to be reuse the class in a forage is to have a get enumerator method on that class that's it and this is checked at compile-time okay so at compile time it's not looking the compiler to see if that class implements some interface the comparators is does this thing have a method called getting numerator with this signature that's it and once you do that you're home free so what I've done to implement that literally is I've got a method here that says get a numerator is public in this case it returns an AI numerator of movie and that's how I've implemented it I don't have to have so many items in here so let me just reduce the number of items so you can kind of see it all together but then want to show you the compiled code for this so you get a better sense of what's happening so that's what all I've done now this is a feature in c-sharp 3-5 that was implement us in c-sharp 3.5 the iterator pattern with the e return so now it's a lot simpler then if you look at my so you think you can link with you this is a lot of work that needs to be done however I'm going to show you the kind of work that needs to be done by sort of decompiling this from IL back to see shops you'll get a better sense of what that is someone will take this I'm going to copy this into sharp lab after i/o so if I scroll down a bit more this is all come to get movies method it's normal and now the magic will start so right here we didn't write this class the movies class even though we wrote the actual class this is the part that we do not write is a nested class that's private seal and it implements the animator with off of movie and it's got all the same permutation in here this is code that we never wrote but it was compiled to this code it is the c-sharp luring feature in this case of getenumerator so just because you see a method called get enumerator when I'm trying to get across to you is the even though that's just another method that method has doing a lot of work as potential so creating a new instance of some class that is disposable so it's not coming for free and that's the reason why the foliage is slightly slower than the for loop and behind the scenes the he'll return is translating to this so it's made our life simple basically all this code that is compiler generated at one point nothing before c-sharp three five we had to write this code ourselves for the class that we wanted to iterate over without it being kind of automatic now the irritants lot easier but the compiled code is still all of this stuff so this lot of code but there's not a move easier but reset method is here the getenumerator method is here then we have the state machine to get the move next and the current and the state machine I see is so happens it's very similar to the state machine that gets generated for us when we do async/await but that would be a another video so is this short and sweet I want to make sure I don't miss guide people into thinking that he should be using a for loop rather than a for each loop but as you can see that even a for each two can potentially translate to a for loop under the hood and or sometimes it uses they get a numerator method for some objects and sometimes if you want to have ill and many more build your own it reading methods are you trading classes where you want to use them in a for loop you can do that simply by having a method called get enumerator that it does an ienumerable of some tea that you have and that's it before it just works for you isn't that cool all right so this brings us to the end of this video and I will see you next time
Info
Channel: Shiv Kumar
Views: 1,524
Rating: undefined out of 5
Keywords: C#, .NET, OOD, OOP, IoT, Arduino, Photon, Esp8266, .NET Core, ASP.NET, ASP.NET Core, Azure, Azure DevOps, CI/CD, Azure Build Pipeline, Azure Release Pipeline, Programming with Intent, Code Reviews, Code Reviewer, Value Types, Reference Types, Passing by value, Passing by reference, strings, immutable strings, Delegates, Higher Order Functions, Functional Programming, for, foreach, C# Lowering, BenchmarkDotNet, Immutable
Id: 9bTpI86bA5E
Channel Id: undefined
Length: 18min 11sec (1091 seconds)
Published: Sat Mar 14 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.