Working with memory mapped files in .NET 4

I have been exploring different new features that come with the .NET 4, beyond the most popular ones like dynamic types and covariance; I was interested in performance enhancements. For this reason I am going to publish a couple of blog entries were I explore these different features.

Memory mapped files may sounds alien to the managed code developer but it has been around for years, what is more, it is so intrinsic in the OS that practically any communication model that wants to share data uses it behind the scenes.

So what is it? A memory mapped file allows you to reserve a region of address space and commit physical storage to a region (hmmm, sounds like virtual memory, isn’t it?) but the main difference is that the physical storage comes from a file that is already on the disk instead of the memory manager. I will say that it has two main purposes:

· It is ideal to access a data file on disk without performing file I/O operations and from buffering the file’s content. This works great when you deal with large data files.

· You can use memory mapped files to allow multiple processes running on the same machine to share data with each other.

The memory mapped file is the most efficient way for multiple processes on a single machine to communicate with each other. What is more, if we check other IPC methods we can see the following architecture:

 

Impressive, isn’t it? Now you have the power of this technology available on the System.IO namespace (instead of using the Pinvoke approach).

Now let’s quickly explore how it works. We have two types of memory mapped files models, one model is using a custom file, this can be any file that the application accesses it and the other one using the page file that we are going to share it with the memory manager (this is the model that most of the technologies above use).

Let’s explore the custom file model. The first thing that we need to do is to create a FileStream to the file that we are going to use, this can be an existing file or a new file (keep in mind that you should open this file as shared, otherwise no other process will be able to access it!). With the stream in place, we can now create the memory mapped file. Let’s see an example:

using System.IO.MemoryMappedFile;

MemoryMappedFile MemoryMapped = MemoryMappedFile.CreateFromFile(

new FileStream(@"C:\temp\Map.mp", FileMode.Create), // Any stream will do it

       "MyMemMapFile", // Name

1024 * 1024, // Size in bytes

MemoryMappedFileAccess.ReadWrite); // Access type

I have use one of the simplest constructor, we define the stream to use and we provide a name. The object needs to know the size of it in bytes and the type of access that we need. This will create the memory mapped file but to start using it we will need a map view. We can create one using the following syntax:

MemoryMappedViewAccessor FileMapView = MemoryMapped.CreateViewAccessor();

This map covers the file from the first byte until the end. If we need now to write or read information from it we just call the map view methods with the correct offset.

int Number = 1234;

FileMapView.Write(0, Number);

FileMapView.Write<Container>(4, ref MyContainer);

We can see that we can write built in types or custom types with the generic version. The good thing about the memory mapped files is persistence, as soon as you close it the contents will be dumped on the disk, this is a great scenario for sharing cached information between applications.

Now if we want to read from it, the other process needs also to create a memory mapped file, we can use the other static initialize that opens an existing one or creates one if it does not exist.

MemoryMappedFile MemoryMapped = MemoryMappedFile.CreateOrOpen(

       new FileStream(@"C:\temp\Map.mp", FileMode.Create), // Any stream will do it
"MyMemMapFile", // Name

       1024 * 1024, // Size in bytes

       MemoryMappedFileAccess.ReadWrite); // Access type

Create the map view and read it:

using (MemoryMappedViewAccessor FileMap = MemoryMapped.CreateViewAccessor())

{

       Container NewContainer = new Container();

       FileMap.Read<Container>(4, out NewContainer);

}

That’s it, really simple isn’t it? Now, there is a small drawback with this approach and is related to the size of the memory mapped file. If you don’t know in advance you will need to create a large file
“just in case”, this can be a very large file with a lot of wasted space, it would be nice to have the ability to reserve the space instead of committing all of it, isn’t it?

In order to solve this issue you can use the page file, this has the advantage of allowing you to commit data on the fly but introduces another issue: you don’t own the file and the map will last until the last handle is destroyed. But think about it, for certain scenarios this is absolutely valid.

Now if we revisit the sample we will need to change some initialization parameters, for this particular one I will use the full constructor so I can introduce some other features that are also applicable to the custom files one.

MemoryMappedFileSecurity CustomSecurity = new MemoryMappedFileSecurity();

MemoryMappedFile PagedMemoryMapped = MemoryMappedFile.CreateNew(

       @"Salvador", // Name

       1024 * 1024, // Size

       MemoryMappedFileAccess.ReadWrite, // Access type

       MemoryMappedFileOptions.DelayAllocatePages, // Pseudo reserve/commit

       CustomSecurity, // You can customize the security

       HandleInheritability.Inheritable); // Inherit to child process

The memory mapped file security allows you to customize who or which process can have access to the resource, this can be quite important when you want to protect sensitive information and you don’t want other processes changing the file map. You can explore that object and see all the different settings that you can change. The reference is here. If we explore the construction of the memory mapped file we can see that there is no stream, we just name the resource. This will create an association between a section of the file based on the size and the map name, this is how both processes will access the file. Note as well that I am setting the property “DelayAllocatePages”, this implements a pseudo reserve/commit model where we will use the space once is needed. Finally, the other interesting parameter is the handle inheritance model, this will allow to share this resource with a child process if is required.

The access to the file uses the same syntax as the previous example, remember that if you close the memory mapped file this will be non accessible, this issue catches many developer.

Finally, another interesting area is the creation of multiple map views, these can work on the same memory mapped file accessing different areas of the files. This will allow you to properly protect the content and allowing you to synchronize the access.

You can do this defining different offsets and lengths when you create your map view.

MemoryMappedViewAccessor WriteMap = MemoryMapped.CreateViewAccessor(0, 1024,
MemoryMappedFileAccess.ReadWrite);

MemoryMappedViewAccessor ReadMap = MemoryMapped.CreateViewAccessor(1025, 1024,
MemoryMappedFileAccess.Read);

Now you can enjoy the power of this high performance data sharing model on your applications when you compile them with .NET 4.0! Note that this blog is based on beta 1 information, I will check the post once is released.