Ref struct, ref readonly struct, ref returning, ref everything, Konrad Kokosa

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I am really happy that I'm here and I'm really happy that you are here so let's just make some meaty meaty presentation which will be lost hardcore keynote you have ever seen have you ever seen dotnet key not about assembler saying assembly code in keynote one pick one where I have a concurrent guy which is talking about this okay so this is a my book as you see it it is quite big and you can win it listen carefully I will ask a question at the end until we can win this signed copy of it so listen carefully simply I will ask at the end okay so as you said my presentation is about ref everything so let's just get part we started a film we fuse the words about me I wrote this book I'm on Twitter please follow me I'm have a blog I'm a kind of co-organized co-organizer of dotnet o Sphinx which is a initiative of people which are talking about dotnet performance so you can look for us maybe we will do it also something here in Belarus and have you ever played any card game I Magic the Gathering hearthstone something like that I'm just designing a geeky Fink card game for dotnet developers so if you like playing card games it will be physical card game about building up and avoiding memory leaks so maybe you will be interested but enough about me let's let's start the most hardcore keynote you have ever seen maybe probably which will contain also assembly code for example let's start from the beginning class versus struct what is the difference can anyone of you can say a few word what is the difference reference a value control a value type it is a main difference between the struct and the class it the fear the class represents reference differences structs is the value itself so for example let's say we have simple experiment we have a program which has a main method it has a run method and we pass helper - we have some helper method which takes some argument which is some type we don't know whether it is class or struct it is some kind of type and we are using this type we are accessing one field inside and yes there is a center here when when the when the when the some type is a class the code looks like that it doesn't make any sense maybe to explain it directly here but the main issue is here that it is passed by reference so not no matter how big is this type no matter how big is this class we are just passing a reference to this object you know that for sure if the some type is a struct we are just passing a copy of this struct somehow we can say it's a value itself so passing struct by the as an argument makes it that we are passing the whole struct it can be using registers or stock but in the it makes in only the most important thing is that it really is just passing the value itself so for a small object like that it's okay if we make this object bigger things are getting worse I mean the class is bigger but the code looks like exactly the same because we are passing the reference it doesn't make doesn't matter what the type is big or not but in case of structs it I as you see the code is much much bigger because now the struct is big and the compiler JIT compiler says that okay we have to pass it as a copy and we have to use stack to copy the whole struct so we are seeing here copying the whole structure through the stack simply so it is a lot of code and it takes a lot of time to copy this structure through the stack so what we can do what we can do to avoid this such kind of coping we can use ref and as any of you have used ref any time so far so as you see and as you used there is something like graph which is a passing that by reference and the code is now a little bit smaller because we are passing the reference to this structure it doesn't make it doesn't matter how big the structure is we are just passing the reference to it so it is very nice we are using values value type structures but we can avoid copying by using references and it is nice so this kind of ref as a perimeter was in c-sharp for for many many years and a lot of you probably use it as you raised your hand because it was from the beginning of c-sharp and underneath such by reference uses something which is called manage pointer which is also quite called by ref because it is passing by refs of it is kind of another name and but underneath it is using manage pointer and such manage pointer has such has some characteristics it's strongly typed so it points to a specific type like in a regular pointer in C++ so maybe it's not so fancy managed pointer because as we have reference regular reference in c-sharp it only in can only point to a object but when it's pointer can point to a lie a lot of different things like as you see for we can point to a local variable it can point to a method argument object field inside field it is interesting and also inside an array it can represent an array element so we can point through a managed pointer a specific element of an array and as is this it also its limited Vanek we can meet - pointer well it can live and can live as a local variable method argument and methods return value which is quite important because as we said managed pointer can represent local variable so it is quite time limited I mean look at viable has a lifetime limited to the method itself so because of that - pointer also has a limited lifetime it can also live only as a viable method argument and rater method value but it can't live on hip it's critical that the managed pointer can live on the hip we can and can't land on the hip because it will be a problematic because imagine that we have a managed pointer living on the hip with some lifetime and it points to local viable but this local variable probably is very limited and it will just die after the method ends and what I would shoot the managed pointer point to them it's simply not possible to represent trocar viable on the managed pointer on the hip so since this C sharp 7.0 Microsoft is putting a lot of love into the ref because they provide possibility to write fast code because of avoiding copying using value types and the JIT also nicely optimizes using graphs so the adding a lot of things related to ref not only to ref passing by reference as you see as you sew so this we know roof parameters manage pointer using is used to pass parameter to pass type as a object as a reference and it is okay but the story begins now because as I said Microsoft added a lot of things recently recently since the C sharp 7.0 so for example we have brief locals have you ever used reef local only few people nice so you learn about it a ref local is a kind of variable which represents refer it is by reference so now you can say for example that this type this object is a reference to this argument so maybe by itself it's not so useful but we can also go deeper so for example this local reference is a reference to any an integer being the field of other type so now we can go into this integer directly and this is just a pointer so we are using this as a shortcut to a specific field and I think it will be much faster now because it is just a pointer underneath so this is our F flow so for example we can use it for sort of creating some shortcuts to uh some type deep data of the structure so for example if we want to dive into some field and some error insight and the specific element of this array we can create such ref lock our shortcut to this specific element so accessing it now it will be much faster not going through this field and through the index but just we are getting the ref low-calorie lock a reference to a specific field and all right for example so this is nice but this is kind of not the most important thing that they added they had it also possibility to reef return something which was required some tuning of the specification of the il itself because it is not always referred by reef returning by Raph everything we can't return by ref everything for example this code is bad I hope it is quite clear because we have local variable here and we are trying to return reference to it but it doesn't make sense because this local variable will end his life here so returning life reference to this local variable doesn't make sense because after this method ends this reference would point to not nothing I mean it will be very dangerous to represent such local variable by reference so this code is bad but this code will be okay because we are getting an array from outside of the method so from the perspective of this method the array lives longer does that the method itself so we can pass a reference to a specific index of this array for example because from the perspective of the lifetime of this array it is okay so we can return an element of an array by reference like that is it clear at least a little okay so nice the question for you what do you think whether this code is better or not who thinks that this code doesn't make sense and it should not compile okay like the many percent other thing that it should work and a lot of other people doesn't have idea of currently the answer is it is working I mean it is interesting but it is working because we are creating here a local variable which represents an array and we are returning reference to a specific element of this array but this prolongs the life of this array itself so outside of this method we are starting to we are getting a reference to a specific element of an array and it makes it living longer because for now DC will treat this as a root of this array so it won't clean it because we have now from outside of this method after this method ends we will have this one element of the array and it makes that you see knowing that it should not clean it because we have the specific element so it is interesting and this is kind of surprising that it is working and it's required kind of tuning of the runtime itself to make it work and this is in fact called interior pointer because we are just pointing into the object this interior of that object so this interior pointer has to be interpreted by the GC somehow do not clean the array which is represented but by such interior pointer okay so let's look at few examples about ref returning we have classic code without any refs currently which is a book collection of book collection it's a wrapper about around the array of books simply and we are just we have Maya thought which just gets a specific book by title searching through these books and the returning the book which has has this title which specified so and this is book is contribute our class so we are just having this new inkopolis collection finding specific book and trying to change the outer of this book the result will be obviously ok because we are classes are references so we are getting a reference to a specific book from inside this collection and the result will be that I will be changing the specific book because it was returned by reference as all classes are returned by reference what is the result if we would we have read return class it would be exactly the same we are ref returning returning reference by reference which guy is kind of maybe not necessary because we are now have two references we are returning reference to a reference book so from this perspective the result is exactly the same and returning references to references in fact is in c-sharp quite are not very often seen because it doesn't have a lot in return but the result what will be the result in such case when the value book is a struct when the value book is a struct we are returning it but value book is a struct so it's passed by value so we were just returning here a copy of the structure here because it is of structure so in this case the result will be that the original book wasn't changed because we are where we are here modifying the copy of the structure from inside this collection and we can pass it and we can operate on the original value by ref returning it so we can now use this ref keyword store if return a book from inside the collection and assign it to a ref local book and then operate on this reference to the structure inside this collection and now it will work because we are operating on the reference to the particular structure inside the collection so it is good because when we have a collection of value types which have a nice for example memory locality and we would like to operate on such collections we can brief return specific elements and then operate all need without copying open I think on this particular element without coping and this is nice benefit of it ref returning so it was kind of from c-sharp 7.0 added four scenarios so because of that probably you will hear a lot maybe not a lot but more and more about riff returning collections what you have collection which allows the reef returned its elements but for example even indexers can Rev return but as indexers are already taken and have they already agreed semantics we can't reuse indexers in lists or other collections so it was agreed that method the standard name of the method which returns by reference will be item ref and you will have item ref which returns elements from a specific collections and for example all collections in system and immutable collections already implement item ref so you can return riff return element from immutable collections which is nice so this is one thing another thing more advanced one for even more nerdy and if you have ever used ternary expression there is rafter nary expression even added in the newest c-sharp so instead of writing a regular a ternary exception you have you can expression you can use refs and it allows you to write quite animatic let's say in cryptic code like here for example you can of course pass as a parameter result of ref ternary expression or you can assign to a result of a rafter now expression so read the code looks quite strange like that because this assignment is on the left side and we are just assigning this text whether to this one and the or this field depending on the condition so it allows us to write kind of it's not very popular thing probably for people using rafts a lot maybe it will just shorten the code but yes this is a example that they are just adding eggs constantly adding more and more things related to tariffs for example also there is ref for each so we can ref iterate by reference all elements in collection if current method of enumerator ref returns something so we can do something like that if we have a collection and we would like to iterate through it as through references you can write for each Rath something in something it only requires that the current method of enumerator ref returns simply which may be or may be not useful but again it is example as you see they added that in the newest version of c-sharp currently available c-sharp 7.3 so currently there is no any API I'm not aware of any public mad and public library using it but still we can expect it will be showing up in the future but as you revs in general are right and treat I mean we are just getting reference and we can do anything with this reference because it allows both reading and writing so then they added also read-only semantics to have something that is only to read and not write and for example also in the quite numerous c-sharp the Edit ref only read-only refs which is just reference that is read-only so it all this allows modifying it also very important to make sure that you understand that what is protected by writing I mean if it and it depends what it is represented by the by this manage pointed by the ref if it is a reference type so if you are using it to pass class the reference itself is protected so we can't write we can't modify the reference but the object pointed by the reference is not protected and for value types so for structs the value itself so destruct itself is protected from writing we I will show you examples soon and this read only semantics is available in two via two keywords for return value tie for return return values and four local variables it is our afraid only keywords and for method parameters it is in keyword maybe it was not very fortunate to but decision but it is like that so we can which we have references type reference so we ref returning book which is a class which is a reference the only thing it is protected is the reference itself so we getting the reference to a book and we can change the reference but we can change the object itself we can change its fields and so on and so on because only the reference is protected not the object itself in case of value types like structures the protected thing is the structure itself so this won't compile at all but we can also modify the structure itself because it is RIF read-only book so we can't change any fields of it because compiler will won't allow it so it is a nice combination of thing that we have something passed by reference without copying but it is also protected from modification which can be useful in some scenarios like that point 3d is a struct because we would like have it small we have static field which is origin and we would like to use it but without copying and because we would like to avoid copying we would like to use Rath but we want allow changing it because it would be strange if someone in the our code would change the meaning and the values of point 3d origin so we'd have to be read-only ref to have it without copying and without chances to modify it so it is a good example of read-only rafts and in parameter is a read refereed only parameter is the like the same so if we are passing something as a reef referee's only we can't modify the fields as a value book is a struct it means that we can't modify the book we can't modify any of its fields and so on and so on so it's like not copying things and having it bypassed by reference which is nice and - in fact we can use this in parameters everywhere in as you see for parameters for in lambdas in indexers even in extension methods so we are passing this grid as a referral only parameter so it makes that we can't modify this extension method but we won't be able to modify the object itself so it's just quite powerful mechanisms that you can use in many places in fact but the question is what happens in this code we do you I will give you one minute do you see anything any danger in this cult sorry yes yes yes you are you alert the answered the next slide so it's absolutely we are right the problem the the source problem here is that we are have a struct which has a method which modifies distract itself which changes some field and then we are trying to call it on ref read-only struct so what compiler would compiler should do now if it's seeing that we are calling a method on the read-only reference which tries to modify this object it should not allow it because we did should not be allowed to modify this object in any way so what the decision was because it turns out that detecting such situation the compiler sight is very complex and currently it is not detected the only thing that we can do is to operate on the copy of this struct in fact so if you are calling this method here we are just calling it on the copy of the struct it is called defensive copy let's allow to call it but it will modify some temporary copy of the struct or destructor so it will be quite suppressed so Trust surprisingly for for some scenarios like for example so yes this this won't be allowed and this this can be problematic we can even see in the you know il code that in this there is this copying of this structure happening it is called defensive copy because it defines us from allowing this modification and how to avoid that I will say in a few slides one side notes it is not only for refs in fact it's only it's all for all read-only fields so for example if you if you write such code that is about I don't know updating some balance it is not working in fact yes because we have read-only structure here which we are trying to update here by a 100 but it will update a copy of this destructor so it won't update the originals structure and the result will be zero and zero because this update value operates on the defensive copy it will be also quite surprising for example spinlock is a struct and if someone would like to be clever and cache it like create static read-only spin lock calling on this spin lock will be calling on the defensive copy of this spin lock so it will be entering some temporary spin lock and not the original one which which makes this completely useless so you can also find such issues on github that hey my spin lock is not working that that's because it is read-only I field used as a read-only field we'd only struct is just a combination of this so you have all fields must be read-only and you can't modify struct after creating it so it is a kind of extension we have all fields read as read-only and after creating such struct you can't modify any of its field and this is a very good for if only read because now JIT and compiler will know that if you are passing read of read only reference it can avoiding creating defensive copies because it won't be modified because it's read-only read-only structure so we can avoiding you can avoid in creating a defensive copy so a way of defining a void in defense is copies is to create read only structs and pass them as read-only refs and we will be working together very nicely without creating those defensive copies and we will see in the code that in fact indeed the defensive copy is being reduced so we can pass around a lot of things by reference but whether it is all related to ref no because we have something like ref struct which is even more fans you think the rational about this is that we would like to have a type that can contain manage pointer but managed pointer can live only on such things like local in thinking opposite way we can say that a managed pointer should not only we can say that mana sponte should not not even ever live on hip-hip so we would like to have a type that also would live only on Stark not not ever we would be created on the hip and so so we would contain by reference because by reference will be have the same limitation whether the type itself and that's why it's called by ref like type and so it is it is called refs track in fact so it is a new kind of struct we are adding this refs keyboard introduced recently and we are creating such ref struct which is regular struct but if guarentees that it won't ever be created and it won't ever happen to live on the on the hip on the hip via so have a lot of limitations like it can be a field of another object it can be boxed it can be a static field it can be a field of an array in can implement interface in fact it can it can do a lot of things just because we want to go aren t that it will be never living on the hip and this is very nice for us because now it will be saved that such rough struct will contain also managed pointer so it can be a part of another ref struct it can be used as a local variable as a return type of by read by ref but again anything not to create it on the hip on the hip it gives us two nice things it never will be hip allocated so it is fast and also it will be accessed only by one on one single thread so it also doesn't require any synchronization because it is only accessed by only a single threat it is nice so we can also combine it for example create read only ref struct which is now obvious it will be a ref struct which is only created once and cannot be modified we have also something like where disposable ref structs lie we have this problem here that we would like to have a ref struct which implements I dispose able to be used by using but a refs trucks cannot use and cannot implement interfaces as we said so the decision was quite simple it just is done like that if the method if there is a dispose method inside for abstract it is okay for c-sharp compiler and it can be used inside using clause it is just enough it doesn't need to implement a I disposable and it will be working like that since I see our point eight zero example of that is the kind of wrapper around managed memo unmanaged memory for example so we are getting this in the constructor we are getting referenced on some unmanaged memory and in this pose we are just disposing this unmanaged memory and we will be nicely work thanks to the this disposed handlink another thing people would like to have this because as we said resurrect we created mainly to represent something which contains also by ref instance field by ref managed pointer and this is exactly why span was created we would like to create a type which can represent sub regions of other objects so we need for example sub region of array without copying it so we need to have managed pointer which points inside such array with a given length saying that this is a slice of an array so we had we need to have this managed pointer inside such span and this is exactly like what we would like to have span should contain managed pointer which points inside an array and the length saying how many array elements we represent so we would like to imagine spanis subtype it is a struct which contains by ref which contains a reference type and pointer it doesn't compile it does not support it in c-sharp currently such code because it just doesn't written like that with our compiler doesn't support such such cement such kind of writing the Rif keyword inside the field but it was likes it omitted by using by reference type which is a generic which represents such riff-raff field so it's a field representing manage pointer and this by reference in fact is a simple wrapper around the simple pointer and it is a proprietary represented by runtime inside so in the end span looks like that it's a length to represent how many elements we represent and also this managed pointer represented by this magic by reference type which says from which memory region from which memory array element we start and so on and so on one would like to ask ok if we had something like by reference here I would like to reuse it in my types because currently it is internal and it cannot reuse it and one would ask why why why I would like to use it but in general interpreting such manage pointer as a reference as a field of another type is quite expensive and also we would like we would be able to create some quite cryptic graph references when one object yields points to inside other object field and then this field points another field and now the object draft between objects would be very very complex so in general it is quite more at the one hand it is very complex in interpreting and other hand it is also very complex and introduces quite big overhead to the GC itself so we should rather not expect that it will be exposed to us and we will not be able to use it as a general thing so as a summary a lot of refs you have ref in parameters so you have ref locals ref returns ref ternary expression a lot of refs in different ways refs trucks and I hope that you currently have a little bit more knowledge about what is possible by you all these ref things and as I ultimate example I created I hope I will it will be just few slides value stringbuilder which is a ref struct as you see because I would like to implement alternative implementation of string builder which is a sad type because it is tied at this class it creates it allocates I hate allocating things so we should create something which is as fast as possible and we are creating for abstract value string builder which is nice because it will be created on the stack or in registers it won't use any memory we are providing it some external buffer I will explain some why it also referee turns also I mean the indexer ref returns the specific chars and appending is simple operating on the internal buffer provided from external code filling the successive characters by the thing that we provided so it is from the algorithm of view it is very simple but what's more important it is ref struct it will be access it only from a single thread so we don't have to care about synchronization and yes so we have getting this we are getting this span which represents some kind of initial buffer and then of course this initial buffer can feel if we will be appending a lot of characters so at this point we are I'm using if it will happen I'm using another fancy thing which is our rifle so I'm changing this internal internal internal buffer with an array pole which is which with an array which is shard from our a pool in case if it initial buffer is filled but this is a just handling the case when the initial buffer was filled it has also disposed so it can be used with the using clause which just returns the array if was it if if it was rented and the most sad method is to string because it needs allocate I mean at some point we need string from so we need to create a new string probably for our crowd expect string so we have to create the string down at the end and using it is very nice because it is like exactly like the regular string regular but now we are operating mostly on the stack or registers I'm creating a very small buffer on the start with the help of stock alok keyboard so I'm not touching any heap here there is no DC pressure here because everything happens on the stack or maybe even on the registers because it is able to optimize it and I'm appending some things and at the end the end I'm creating the resulting string because I need the string and that's why value string builder expects buffer from outside because I would like to be able to create stock a lock and pass it inside the star value string builder the difference in the performance is maybe not that's like kicking us our asses but still it is noticeable I mean it is faster and it is also allocating a lot of more less memory because of this and that in fact these bytes are only the resulting string and not inside the string before buffer there is nothing allocated so it may be well if this happening once per date is Mike an instance to make the such type but if it is happening 1000 per second obviously this difference in allocations will be huge for your application and to be even more fancy we can also create such a numerator which ref returns by reef Biograph so the current will Rev return and then with it allow us to make such funny things like in place modifying the string and the buffer itself we can for each now for the particular characters in this in this builder and create character in place modified by increasing ASCII code by one for example is dummy example but it shows that it makes such things possible okay and that's all now I have a question for you regarding how regarding winning these two kilograms what is the name of the pointer which is pointing inside other objects interior pointer you were over fire you're the fastest so I don't know if speakers should attend but this contents but I didn't say anything about that so congratulations [Applause] do we have time for questions yeah we have couple means probably almost five minutes to five questions and here do we have microphone thank you by the way very interesting I didn't know about this possible but that you can dispose ref star Conrad thanks for the overview so what I would like to ask is basically I am not sure if it's working basically we understand that the main point of introducing graph is performance optimization yeah on the other hand I can say that like 99% of the enterprise applications we are writing is not implementing any in-memory databases they just communicating with some kind of storage like I don't know database search engine external services and the performance heat is introduced by those external calls you know unless there are significant design flaws the memory management doesn't introduce much overhead because the objects are small okay on the other hand I can see different areas where performance is critical and any requirement separate in terms of microseconds some like a couple of milliseconds for example electronic trade and they are not using dotnet for like obvious so can you elaborate what kind of applications might benefit from these reps yes I mean yes for sure 90% of applications doesn't require and I would not start designing domain by using references or something like that on the other hand I see that more and more adoption of dotnet core in such scenarios as you said because Microsoft is putt-putting a lot of love to the performance they are blogging a lot of things about that they are able to process millions of requests per second in persona so a lot of companies also are also to like starting to interest in dotnet core also as a high performance platform and in the search scenarios just this is an answer for for such people yes so I'm seeing companies that I try the trying to use it in Big Data when maybe you have a single operation it's not so important but you have so many objects that every millisecond counts for example is gaming other things so yes I there is no specific domain that and company that I could point to that is starting to using crafts and all this stuff but I'm seeing a higher and higher adoption of this as a high performance platform which is nice simply because it now provides such capabilities and I'm very happy with that thank you yeah just one question about array pool do can we use it everywhere can it be by default sink as a back-end for array allocation or we have some constraints of using ripples since I would I would not probably create an array through our a pool because it creates some overhead by finding a good array inside an array pool so creating each array smaller race through a ripple would be probably quite bigger would introduce overhead but the more bigger erase you create I would reuse all right if you are using if the more big you erase you use the more you should be interested in using horrible if and especially if you're creating so bigger ripples that they land in large object heap so bigger than 90,000 elements or so for sure you should by default start using her ripple the bigger array the bigger array the more the sensible is to use it simply but not for very often created small arrays but it will be overhead simply not not giving a lot in return any question more questions okay I have a question can have you heard about redundant methods about what read-only methods yes yes there is an ongoing project of read-only matters because as you said you can mark the whole struct as read-only but it puts a quite big limitation because maybe not the whole struct makes sense to read only because it blocks for modifying it and there will be a probably feature which will allow you to mark only a single method as a read-only and then it will know that this particular method is read-only and we won't modify this structure so I can avoid creating defensive copy thank you very much thank you very much you
Info
Channel: SPACE
Views: 1,161
Rating: 5 out of 5
Keywords: dotnetby
Id: touhHCD7RIc
Channel Id: undefined
Length: 44min 56sec (2696 seconds)
Published: Mon Jun 24 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.