 A very good afternoon to all listening to our session on Zero Copy Zen, booth performance with member view. So speakers here are myself, Kessia Mayujois. I may be M. Joseph. We are product engineers at UST Stolby. At Stolby, we are building a travel platform to bring innovation to the way people experience travel, and we are majorly working on GraphQL microservices in Python. So I hope by the end of the session, it will not be a zero copy of knowledge, but you'll all gain some insight on zero copy and memory view. So without waiting any ado, let us move into the main agenda. Now what is zero copy? Well, zero copy is something that as a name suggests, no copy of the data. That is the CP does not perform the task of copying data from one memory area to another. So what happens even to the unnecessary data copies are avoided. Well, you can say is a method to copy data from the disk or network to the memory without passing through CPU. So what's the main advantage is that it will reduce the number of context switches between the kernel and the user space and thereby increasing the efficiency and reducing the time taken. Now if I see a simple analogy, I can say that imagine you have a sandwich and your friend would like to have a share of it. Now in a tradition, I can say that you create or you make the same sandwich with the same ingredients. Like I was the same replica of the same sandwich that you were eating and give it to your friend. So that way your friend could have a share of it or another mechanism. It could be like no copy of the sandwich. So no zero copy would be like you give your sandwich a bite to your friend. So what happens is what was the main objective? Our friend would want to have taste of sandwich and that is achieved. So we are creating any intermediate copy but by just sharing the same sandwich to you with your friend, the objective is achieved. So that is a simple example of what is zero copy. Now let us look into a traditional copy mechanism. I can say that you have a user portal and you would like to put your resume into it. Well, like a normal user, you have your resume in your secondary storage. Now what happens here is you have your data that is a bit of a real data needs to be loaded from your secondary storage to the memory by the kernel and then this information is then sent to the user space or to the browser by the kernel. Now the browser that is in the user space, it shall now use their socket related function to transfer the information to the corresponding socket dedicated for the job portal. Now eventually you can see the socket redirect this information to the kernel which interacts with the networking hardware using the DMA that is a direct primary access. The at the end goal it is achieved. So what happens even if you look into this figure in this architecture set, we can understand that there are almost two context switches and there are four copies of data created that is from the disk to the kernel space memory from the kernel space family to the user space memory and then back from the user to the kernel and the kernel to the networking hardware. Well, you can see the context switches from kernel to user space and user to kernel space. So what we can see can conclude by the tradition cancer is that it has a serious hurdle in the performance as the process based the CPU cycles. So that's the main disadvantage of the traditional cancer. Now we look into the zero-copy mechanism in a zero-copy mechanism where you can see that what if the kernel self handles the copy of the data from the disk to the network and directly that is we are reducing the number of context switches and the copies of data created. So if you see in this example, you can see that at the end the user is directly accessing the kernel of the memory or the buffer in the kernel space. So we are creating a copy but the user relies directly on the intermediate buffer. So this is something you can see about zero-copy mechanism. If you look into the code, we have a Python inside the code in the first code, you can see that it's a receiving server and in this receiving server code. We can see that stock it first establishes connection. Here we use AFINET connection. AFINET it is of the IPv4 address family and of the SOC stream socket type. So here we would be listening to the port 8082 or establishing connection on that particular port and then we'll be alternating till we receive some data. So we are receiving data of around 65,536 bytes of data in one go, that is a chance of data being sent. So the maximum data limit is that particular size and I mean this is my receiving end server. So I wait for incoming connections. Now if you look into the next part that is once the connection established, this is like the sending part using the traditional copy client. So using the traditional copy clients, you can see that you use send all the cans here send all is something. It's the main functionality that uses it that is present in the traditional copy client. Well, in this send all the entire file content is be sent to the server using the socket send all metal. So you can see a copy of the data that's been created and it's being transferred. Now, if you look at the while testing it, you can see that a two GB of data file is being used for testing this particular code. So on testing with this two GB file, we found that around 2.42 milliseconds for being taken to transfer this particular size of data. So as the size of my file increases, the time taken would even really increase. That is very obvious, okay. Then through the next part, if you check the zero copy client, in zero copy client we have over start send file. Now in over start send file and as a send file is a mechanism of West where here data has been efficiently transferred from one file descriptor to other without any intermediate copies created or without any intermediate, without the need of intermediate buffering. So the function is particularly useful in instances when you deal with large files or optimizing the data transmission transmission between different are your resources. Now I'm testing this file or testing this particular code with the same two-degree file size. We could, we did realize that around 1.16 seconds for being taken. So if you compare, you'll say that zero copy definitely has a significant reduction in time taken compared to the traditional copy mechanism. So as discussed earlier by the previous analogy, we knew that zero copy beyond dealing with any copies but dealing with the same set of data thereby the time has been significantly reduced. This is a simple example of zero copy in the cancer. Now in Python, we can use this zero copy by using memory view. So let us deal into more of this with the discussion by Abby. Over to you. Yes, Kasey, continuing from there. So let's take a look at how Python's on data types can be useful to work with binary data. So Python provides several built-in types and modules to manipulate binary data efficiently and effectively. So here are some of these bytes, byte arrays, memory view. So these are data types. These types are crucial for managing binary data, allowing us to effectively interact with files, network protocols, and lower-level data structures. So coming to each one, bytes. So bytes are immutable sequence of bytes in Python, representing binary data. So it's useful for storing fixed collection of bytes such as data read from a file or received from a network circuit. So byte will return a byte object. So to form a byte object in Python, we utilize the B perfect accompanied by the actual bytes. Since bytes are immutable, the contents cannot be altered after formation. These characteristics guarantee data integrity and security across diverse applications. So byte data represent a sequence of bytes. Each byte can hold a value in the range of 0 to 255. So coming to the syntax of bytes. So bytes function takes up to three optional arguments, source, encoding, and errors. The source parameter can be provided in different ways, depending on this type. So if the source is an integer, it creates a byte object of the specific size with each byte initialized to the default value, 0. So if source is an interval of integers, it creates a byte object from the integer's value. The encoding parameter specifies which encoding to convert string into bytes. The default encoding is untf8. And the last, the errors parameter determines how to deal with errors during this encoding. So while observing this code, data equal to bytes of 4. We are creating a byte object with length 4. So however, since bytes are not initialized with any specific value, the default value will be 0. And the type of data is bytes. So here in this given code, we are creating a byte object using the constructor that takes an interval of integers as input. So since bytes are immutable, it does not support assignment operations here. So the next one is byte array. Bite array is similar to bytes, but immutable. It allows us to modify the binary data after creation, making it most suitable for situations requiring dynamic changes. So byte arrays are created using the constructor byte array. So since byte array is immutable, we can modify individual bytes or even extend its length if required. So here we are creating a byte array with value 4 since similar to the previous example, since we are not assigning any specific values to byte array. The default value will be set by the byte array constructor that's 0. So we can access and modify the individual bytes within byte array using indexing or assignment operations. So here the value 10 in the array is replaced with value 100. So coming to the last one, that's memory view. It uses serocopy view. So memory views are powerful but often underappreciated feature of Python that provides efficient access to data, particularly while dealing with larger datasets or binary data. So it acts as a window into the underlying data of another object such as bytes byte array or even array rotor. So it allows us to access and manipulate the data without creating a copy. So it provides support for slicing just like regular sequence in Python. So memory views can be created using memory view function which takes an object that supports buffer protocol as its argument. So in this example, we are creating a memory view of byte string hello world. So here view of 0 will mark to 104 which is nothing but the binary or nothing but the ASCII value of H. So we are creating byte array with encoding specified as UT of 8. So we can find that type of MV will be memory view. So here we created memory view with byte and here we are creating, we are creating memory view with byte array. So byte memory view uses buffer protocol. So over to Kasia for buffer protocol. Yes, memory view uses buffer protocol. Now what is buffer protocol? Buffer protocol is a protocol that provides a way to access the internal data of the object and that object can be a memory array or a buffer or so and so. So as I said buffer protocol, like you have an object and that data needs to be accessed by another object. So with our previous interview copy that data could be made accessible to the other object. So the memory representation or the internal memory representation of that particular object can be made, it's made possible to be transferred to another object without creating intermediate copies. Now in Python, if you can see it buffer protocol or if you normally need to perform different operations like slicing, we create copies of the data. Now if you look into simple Python example, you can see that in this particular example, you have a list and it's slicing the list. In the normal use case, what happens is on slicing a list, Python normally it creates a new, what, a new sliced list object. So a new object has been created for performing slicing operations. So maybe in some cases where you have a few set of data and you do not require a new object to be created but such instances, I feel that copies of data could be seriously avoid. Well, in Python or we cannot directly implement buffer protocol. Why? Because this is a present in the CEAPI level. So buffer protocol is not directly implemented. Well, it is made possible or we can directly implement buffer protocol using the memory view library, which is present in Python. So in Python, we are able to implement buffer protocol using memory view. So there are different benefits after using buffer protocol. It is that it improves the execution speed since we are not creating copy. Definitely it improves the speed. The next is he uses less memory for accessing the data and thereby improving efficiency. And the third one would be that it works on large data as I said. Well, if you deal with buffer protocol, there are two main terms that you would be familiar with that is buffer providers and buffer consumers. So objects that provide this memory views that provide memory view are called the buffer providers and objects that consume the data of other objects can be known as buffer consumers. So these are some terms that we would be familiar while learning about buffer protocol. Next dealing with PEP 688. So PEP 688 is a Python enhancement proposal that says that making buffer protocol accessible in Python. So we are making sure that memory view and the benefits of using the protocol is made aware to different Python developers and making it much more user friendly. Well, Python, well PEP standard 688, it promotes the use of memory view at this memory view dot from underscore buffer is a new built-in library to create memory view much more easier. So it makes sure it is, it can be widely used. Then it has enhanced the support of memory view and it has simplified the workflow of easy access to buffer protocol within the Python code. So in a way, so using this PEP standard it has improved or boosted the performance and increased the efficiency throughout the program. So that is about PEP standard. Now, if you go to the next, we know that there are different applications on memory view so that you can dive more into memory view by learning from AB over to you. So coming back to our discussion on memory view. So let's look into this snippet. So here we encounter two variables s1 and s2 with byte and byte array values. So we proceed to create a memory view instances namely s1 view and s2 view from these corresponding variables. So the initial print statement examines whether the memory view originating from s1 that's containing bytes is designated as read-only since bytes are immutable. So following that the subsequent print segment verifies the read-only status of memory view generated from s2 containing byte array. The mutable nature of byte array in Python grants the memory view both read and write privileges to underline that. So in this code you can create byte array s2 containing the bytes hello world. So then you create memory view of that s2 that's s2 view from the byte array hello world. So next you can slice the values. You can assign the Python value with the string world. So here memory view allows this modification because byte array is mutable. The output of our final print statement will be hello Python showing that the modification was successful. So since memory view object s2 view references the same buffer or the same memory updating in s2 view also updates in s2. So moving on we import the module array and create a signed long integer labeled A using the function array dot array with L. Subsequent memory view is also initiated with this array A. So upon invoking m of 0 the first element of memory view is extracted. Similarly m of minus 1 retrieves the ultimate the final element of memory view moving to the terminal element of array. So furthermore we can also provide slicing operations with memory view. So let's look at the comparison over to KCM. So looking to which is that we have learned about what is memory view and so looking into the difference or the speed and which is much more efficient to turn memory view. We have a particular snippet and in this example we are creating through a chunk of data that is of varying size from 1 lag to around 5 lag. The data has been iterated of varying size. So I created data in the first case of a byte into the data has been multiplicated to that in times and then we have what happens next is the data is iterated. The data has been iterated by slicing each time. So I slice the data from first index to the end and as we know with the memory view on slicing it creates a separate object. So I would like to compare the time at that particular case now with that with memory view. We are dealing will be slicing on the memory view of that particular object. So as we know memory view would not be creating a new replica or a new copy of data, but would eventually work on the same data. So this is with memory view example now looking to the time difference. We can see that with our memory view takes around from 0.24 to across 4.3 seconds are being taken with our memory view or with memory view. You can see that almost the entire time is almost approximately equal to 0. That is even though the data has been increased the time limit is significantly less on comparing with without memory view. So this this actually shows it can be concluded that with memory view the time is significantly less. Now if you look into the graphical representation, you can see that the blue line denotes without memory view and orange line denotes with memory view. So it is obvious that as the size of the bytes increases the time taken will definitely increase on if you work on a data without memory view, that is to work on this data by creating replicas over and over again. Now when you work with memory view, you can see the time taken almost equal to 0. Well, there are different applications of memory view in our day to day life. Well, once this application that I can talk about is numeric and scientific computing. Well, we all know about NumPy and SciPy libraries. Well, there are some libraries that we use significantly on dealing with a large arrays or large data set. So memory view if you use memory view on such a data set, it will efficiently improve our calculation and transformations on arrays without copying the data. Well, if you look into this example, in this example, we're dealing with a particular large size array and we're creating a memory view of this array. So the main objective of this particular snippet was to double the values of the array of that many chunks of data present that huge size array. So I'm doubling the values each time. So on dealing memory view without creating copies, I am able to transform the data. So this will reduce a speed and increase the performance. To learn more about other applications, I would like to hear from Ebi. So let's take another example of video streaming. So video streaming in Python using memory views can be achieved by liberating the power of memory files and NumPy arrays. So it probably makes access to data directly without copying it. This can be useful for efficiently streaming larger video files without consuming too much memory. So let's take an example of a media server. We are creating a media server to stream videos to user over the network, granting them the ability to view the real-time content without the need for any prior downloads. So a pivotal element of the system involves granting users the capability to govern video playback, permitting them to advance or rewind, thereby giving them the freedom to skip or revisit the particular segment. So to realize this functionality within the client application, one can accomplish it by making a request to server for a particular data segment that aligns with the time index chosen by the user. So here we have two functions in the first function, time code to index. We can implement the logic to convert the time code into the byte offset of the video. So this calculation will depend on specific video formats and how time codes are mapped to the byte offset. So in the second function, request chunk. So we can implement the logic to fetch video data from the given video, the given offset for the specific size. So this will involve reading the video data from a file or it can be from a remote source depending on our use case. Also throughout this example, we set the value of offset to be 20 megabits. So the effectiveness of this code will rely on two primary elements, the duration for extracting 20MP video segment and the time required for the socket to dispatch the data to the client. Assuming the second factor that's the socket speed is practically ideal after benchmarking, we can see that it took almost 4.9 millisecond to extract 20MP slice of data to transmit to the client. So this problem is that we are slicing when we are slicing a byte instance courses underlying data to be copied, which takes a CPU time. So a better way to write this code is using a memory view concept, which exposes as I think high performance buffer protocols to programs. The buffer protocol serves as a lower level CFA that allows Python runtime and C extensors to access the underlying data buffers behind objects like byte instance. The key advantage of memory view instances that they are sliced, another memory view instance is created without initiating a copy of underlying data. So let's create memory view that wraps bytes instance and examine the slice. So by leveraging the zero copy mechanism, we can see we have significantly enhanced the speed of code that needs to process a large amount of memory quickly. So we are almost at the end of the presentation of zero copy. So now let's discuss on benefits of zero concept copy concept through KSA. Okay, so on the benefits, we know that we have heard that it can reduce the memory footprint by not creating any intermediate copies and the overall memory usage can be reduced. The next we could say that the next benefit would be that it improves the performance. Okay, yeah, it improves the performance by avoiding data copies. You can significantly reduce the time and resources required for data processing. The next is it improves the scalability, especially when dealing with distributed systems or multi-core processors. Scalability is one factor that you will all look into and this would use memory view. It would significantly improve scalability. The next is I can say that in conclusion, I would say remember that zero copy technique is not just about saving a few milliseconds from your code, but it will help us to ensure that we all think critically when we deal with applications like media streaming or maybe networking or other such applications that deal with large data sets. So we should all be aware of when to use memory view. Then thank you all for listening. I hope you will all look into memory view and get into just a bit. So happy coding and have a nice day. Thank you.