With Whidbey Beta 1, we’ve done some performance optimizations for Streams & byte’s stored in .resources files. The optimizations for byte’s are the relatively obvious ones – don’t use serialization, etc. The interesting change happened for MemoryStream. Instead of serializing a MemoryStream then deserializing it at runtime, we generally memory map the .resources file, then return an UnmanagedMemoryStream pointing to the block of memory memory mapped in from the file. This way, we avoid copying data into the GC heap, reducing allocations & improving your application’s working set.
Additionally, if your application is sophisticated enough to deal with a pointer to memory, the UnmanagedMemoryStream allows you to get a pointer to the data directly without having to go through the Stream methods. This can offer significant performance advantages in a number of scenarios when you are reading data structures from a file.
An app can use the new method GetStream on the ResourceManager to get back an UnmanagedMemoryStream pointing to their data. An app may choose to be ignorant of this behavior and simply treat that UnmanagedMemoryStream as a Stream, like this:
Stream s = resMgr.GetStream(“My blob of data”);
Similarly, you can use ResourceManager’s GetObject method instead of GetStream if necessary.
The surprising part about this change is if your application stored a MemoryStream in a .resources file, once you recompile your application on Whidbey, it will be returned as an UnmanagedMemoryStream instead of a MemoryStream. Again, treating the data returned from ResourceManager’s GetObject or GetStream method as a Stream instead of a MemoryStream will ensure you don’t have any problems here.
One note on the memory returned from GetStream & those who will happily use pointers to tat memory – for the cases where we can memory map .resources file, the lifetime of the memory is the lifetime of your application domain. However, the ResourceManager also supports this now-silly-seeming functionality of loading standalone .resources files, via the CreateFileBasedResourceManager method. This used to be how we recommended people use the ResourceManager back in V1 before assemblies looked like a real feature of the CLR. That has changed drastically in the last five years, and this approach based on loose files with no strong name verification isn’t a great idea anymore. But, I made the GetStream method work in this case. Here, we don’t memory map the .resources file, but we instead read from it using a normal FileStream. Internally we read into another buffer and pin it for the lifetime of the Stream returned from the GetStream method. (If anyone looks closely at our source or the Rotor source, you may notice a new internal PinnedBufferMemoryStream class.) If you use the CreateFileBasedResourceManager method anywhere in your code, you’ll want to be very careful to keep your Stream alive for a while, either by storing it in a static or instance field for some appropriate type, or in some cases, perhaps by very careful & judicious use of GC.KeepAlive (understanding of course that GC.KeepAlive simply is a technique to convince the JIT you still want a reference to that Stream to stay alive until you call GC.KeepAlive).