Friday, January 15, 2010

That algorithm is so hungry!!

Time to write an hungry algorithm in C#. Ok, I did my best to reduce its complexity but data to process was so much that I couldn't do any better than the first attempt. Low DRAM prices helped a bit to cut down costs but it was not the complete solution to the OutOfMemoryException issue that, from time to time, was ruin the game.



What can you do if  a program experiences a OutOfMemoryException the 20% of times you execute that piece of code?
If you're in a situation where your algorithm requires many objects that occupy a lot of memory, you could take advantage from the MemoryFailPoint class that you find in the System.Runtime namespace. The class allows you to check for sufficient memory before starting your hungry piece of code.
To use the class you just need to instantiate an object passing the amount of memory the algorithm you're going to execute would require.
try
{
   // try to reserve 2Gb of memory
   using (MemoryFailPoint mem = new MemoryFailPoint(2000))
   {
      // execute hungry code here
   } // dispose to release resources
}
catch (InsufficientMemoryException e)
{
   // gracefully recover in case of not enough memory
}
The constructor first checks if there is enough space in the page file to satisfy the request. If the space is not available, a garbage collection is forced to try to free up some space. If the space does not suffice yet, it tries to expand the paging file. If the file cannot grow enough, a InsufficientMemoryException (derived from OutOfMemoryException) is thrown. Otherwise, if the space is enough, the requested memory is reserved to a private static field defined within the class. At that point you can run your algorithm with a good chance to have enough memory: it is not guaranteed in fact that reserved memory will be physically allocated to the algorithm. When your algorithm completes, make sure you call the Dispose() method to release the reserved resources.
The MemoryFailPoint class can be a good help to create a robust solution: it's not a guarantee but it helps to gather as much memory as possible providing an elegant way to gracefully recover from a memory issue (for example if the exception is thrown you could decide to split the algorithm execution in two runs and then merge back the results).