Efficient buffering with BufferManager

Fri, Jan 21, 2011 3-minute read

When tasked with writing code that does i/o to read data into a application for further processing, it is normal that a buffer is created that will hold the chunks of data while data is being transferred from the client/disk or what ever medium the data is coming from.

It is not uncommon to find code similar to the example below.

byte[] buffer = new byte[requestSize];
stream.BeginRead(buffer, 0, requestSize, OnReadComplete, null);

While the code above is okay if your application is not very busy, it might be an issue if you have to process a large amount of requests at the same time or in rapid sucession.

The reason for this is that you with the code above allocates a buffer to hold the data, and that buffer has to be allocated, objects larger than 85k is allocated on the large object heap, and if you allocate a lot of different sized objects your large object heap will be fragmented and might lead to out of memory exceptions.

There are a couple of solutions to prevent this issue.

One is to do your own “memory” management and preallocate 10 large byte arrays and reference those from where you need them, and simply re use them as needed. This will prevent a lof of arrays being created and prevent the fragmentation, since those 10 arrays will stay on the same position on the large object heap, thus preventing the fragmentation.

An easier solution is to use the BufferManager class that was introduced with WCF.

The BufferManager class handles the issue with pre allocating chunks of memory and your application simply requests a chunk of memory and returns it when its done with it.

Rather simple

// Create buffer manager with a max size of 1MB and a max buffer size of 100k</span>
BufferManager bufferManager = BufferManager.CreateBufferManager(1000000, 100000);

// Request a buffer
byte[] buffer = bufferManager.TakeBuffer(100000);

// work with the buffer
stream.BeginRead(buffer, 0, buffer.Length, OnReadComplete, null);
// Release the buffer
bufferManager.ReturnBuffer(buffer);

Not only will the buffer manager help migitate the problem with memory fragmentation, it is also much faster to get a preallocated buffer than allocating a buffer each time you need it.

I created a very simple and not very realistic test, to show the difference. The first example uses allocation of the buffers as needed.

Stopwatch watch = new Stopwatch();
watch.Start();

for (int x = 0; x &lt; 1000000; x++)
{
    byte[] buffer = new byte[100000];
    for (int y = 0; y < 1000; y++)
    {
        buffer[y] = (byte)(y % 4);
    }
}

Console.WriteLine(watch.ElapsedMilliseconds);

On my computer this takes 7541 seconds on average to run.

The next example uses the buffer manager but is doing the exact same “work”.

Stopwatch watch = new Stopwatch();
watch.Start();
BufferManager bufferManager = BufferManager.CreateBufferManager(100000, 100000);
for (int x = 0; x < 1000000; x++)
{

	byte[] buffer = bufferManager.TakeBuffer(100000);
	for (int y = 0; y &lt; 1000; y++)
    {
        buffer[y] = (byte)(y % 4);
    }
    bufferManager.ReturnBuffer(buffer);
}

Console.WriteLine(watch.ElapsedMilliseconds);

This example only takes 1390 milliseconds on average to run, thats more than 5 times as fast. Just to allocate the memory.

In real world programs you would not only be allocating memory and doing nothing with it, so the relative performance improvements by switching to using the buffermanager will not be as great as the total time spent allocating memory is probably very low, unless you have a lot of garbage collection going on because of a lot of objects being created and destroyed.

But taking both benefits into considerations, I think it’s definately worth using instead of manually allocating buffers to hold your temporary data.