Span of T vs. Memory of T

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I should give you a fair warning today's topic is the differences between spans of tea and memory of tea and it's not light easy be treating my goal is not to tell you how awesome they are they are is to get you over the hump and understanding how they work I'm going to cover what they do and why I'm going to give you some descriptions of the internal workings and tell you a little bit about fast spans and slow spans finally I I'll do my best to describe the owner consumer model that memory oft uses and why it's relevant when you use these features so as always please subscribe to my channel and leave comments with your thoughts and ideas I'd love to hear from you if you're interested in seeing how spans can boost the performance of a Dapper repository visit my blog better withth code.com and sign up for email notifications just check the box labeled aggregate mapper so I know that's what you're interested in all right back to the main event the purpose of span of te and memory of te is to provide a way to access contiguous regions of memory in a safe and performant way they should be used when you want to avoid memory allocations and you want to get great performance so super quick example here I I'm declaring a string that allocates memory next I use a substring to get the first three characters that allocates memory for another string however if I use the as span method I can get a span that represents the string and get the first three characters without allocating any memory the sign ific of that is that eliminating those memory allocations means that the garbage collector doesn't have to spend time cleaning up that memory great but what's the difference between them so let's start with describing what a span is actually let's look at some pseudo code so here's an example of how a span is defined a span represents a memory region so it has two properties a pointer to that memory region and a length to define the size of the span that's basically it but how they're defined is actually What's significant so a span represents stack allocated memory so can't appear in the Heap why well the life of a span could Outlast the memory that it wraps if the underlying memory was garbage collected we'd have a span that didn't reference what we intended it to and for that reason it's defined as a readon reference struct what's that H down the rabbit hole we go when Microsoft was trying to create spans they had three problems to overcome they wanted to avoid synchronization problems between the pointer to the memory and the length of the Span in multi-threaded situations they needed a way to make sure that a span wouldn't be boxed it wouldn't be stored on the Heap uh and managed pointers can't be used for fields to solve this problem reference structs were created it's a struct that can only live on the stack and there's there's several constraints on this type it can't Implement interfaces it can't be used as generic type arguments it can't be boxed it can't be passed into or used in async methods or iterators so spans are backwards compatible with versions prior to core 2.1 but you do need c 7.2 or higher to compile them which is why there's actually two different types of spans and you'd never know it well because of compiler Magic so with that let's talk about the differences between slow spans and fast spans and we looked at some pseudo code for the definition of a span which was a fast span and here's a picture of what that looks like in memory if we declare a string and we assign it the characters blazing that string gets allocated on the heat the address of that object gets put on the stack and if we create a span to access the data in that string the span will have a byre field that points to the string string along with a length we don't actually hold a reference to the actual string object but slow spans they're for backwards compatibility they can't use by rev Fields so they have to be defined a little bit differently so here's a picture of what a slow span looks like in memory the slow span has to include an object reference the pointer and an offset the object reference is really important because we don't want the garbage collector to go collect the underlying memory so holding a reference prevents that from happening it prevents the garbage collector from reclaiming the memory and that's the one of the reasons why it's slower is because the span has to calculate where an element in the span is located and that calculation using the offset it just takes a little bit longer but don't let that stop you they're still really fast so let's switch gears and talk about memory of t memory of T is used in conjunction with span of T it has some of the same functionality like it can get a slice of data that it encompasses but generally you end up treating it like a box of data that you move from place to place and when you want to get something out of that box you're going to call the span operation to get a span it's kind of like a factory method for spans the biggest difference between memory of T and span of T is that memory of T is not a BF like type it's defined with a normal struct so it can exist on the heat and it doesn't have the limitations or the benefits of span all right so here we have a little code sample of how we can use span of T and memory of t uh actually in this case we're going to use readon memory of T which is just like memory of T except it has no Setters uh so what I've done here is created a file stream object a place where we can go ahead and write some bytes to a file and we're using a a buffered writer we're going to pass in that file stream and give it a buffer size of in this case 128 bytes and then we'll go ahead and create some text and feed that into our readon memory of T and then once we have that now we can go ahead and call the buffer Rider write async method and pass that memory into it the buffer riter class so here we have our right async method now if you remember memory of T can go on the heat and that's necessary for passing uh into asynchronous methods and taking it out of asynchronous methods so that's why we're using readon memory of T in this case instead of a span of T if I were to use span of T it would throw an error and it wouldn't actually compile uh however we've got another method called write and that does take in a span and so that's a synchronous function so when you're using synchronous functions you can pass in spans you want to use spans with synchronous functions if you're going to use a asynchronous function you want to use memory of t or readon memory of T remember when we talked about the notion that our safe and performant way of accessing memory would get messed up if the underlying data was garbage collected well that becomes a real problem with memory of T we need a way to manage the lifetime of the object uh that the memory is pointed out and to do that net Implement a owner and consumer model so I've actually got a little code sample here to demonstrate how this works the concept of ownership really means responsibility to dispose of the underlying memory so when a piece of code is the owner of the memory of T it's responsible for releasing the memory the second part of that model is the consumer the piece of code that holds our reference to the memory is consuming it so it's the consumer and what we need is way to designate which piece of code is the owner we do that with an instance of IM memory owner so here I'm using a memory pool to rent a section of memory memory that can store 64 bytes the rent function returns an instance of IM memory owner and at this point the main function is the owner of the memory I can pass this to do something and at that point that function becomes the consumer because it has an instance to that memory via the variable buffer but now let's look at the function do something and own it the main function is the owner of the memory but I'm passing an instance of IM memory owner into this function which means I'm passing the responsibility to deallocate the memory so at this point once I've passed that IM memory owner into the do something in own it it becomes the owner and the consumer of that memory as part of a best practices you really don't want to use an instance of memory of T after you've passed the IM memory of owner into another function we have to think of it this way when we call do something an own and we've given that responsibility to this function and it's likely going to dispose of it so we wouldn't want to use rental. memory here after we've passed on uh the IM memory of owner into the function because that memory's probably been disposed of and we don't want to access uh deallocated memory all right I think that's a pretty good foundation for the differences between a span of T and memory of t and a little introduction to the owner consumer model uh hopefully I've been able to tell you a little bit more about these two features and giving you some ideas about how you could use them in your own projects uh as always please uh like the video and subscribe to my channel gives me a little bit of feedback on what you're interested in I love to hear your comments so thank you very much
Info
Channel: Jeff Zuerlein
Views: 6,257
Rating: undefined out of 5
Keywords: c#, dotnet, memory, spans
Id: Hb5QPFWm8i4
Channel Id: undefined
Length: 9min 43sec (583 seconds)
Published: Sun Feb 11 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.