In order for a Transport to read and write Messages, it may buffer the serialized message. At the very least, the Headers need to be buffered. And in many cases (such as to avoid head of the line blocking issues), buffering the entire message is necessary.
While the CLR’s garbage collector does a really good job of recycling memory, there are still costs involved with each allocation. These costs include zeroing out the buffer, as well as the GC churn. As our performance dev says “no work is better than some work”. A bunch of Indigo R & D has gone into creating an efficient way to pool buffers given two knobs:
- MaxBufferPoolSize – the total number of bytes to pool
- MaxItemSize – maximum size of an item stored in the pool
With proper tuning, large (>10%) increases in performance can be witnessed. This functionality is exposed through
System.ServiceModel.Channels.BufferManager. If you are writing a custom channel, you can create a BufferManager through the static method:
Then instead of calling “
byte[n];“, you can call “
bufferManager.TakeBuffer(n);“. Just remember to call
bufferManager.ReturnBuffer(buffer) (in a
finally block if necessary) when you are finished. Lastly, you can call
bufferManager.Clear(); if you ever want to flush the cache (e.g. after a certain amount of idle time).
On the built-in WCF Bindings and Transport Binding Elements, we expose MaxBufferPoolSize as a property for you to control our cache footprint. If you are sending small (< 64K) messages, then the default value of 512K is likely acceptable. For larger messages, it's often best to avoid pooling altogether, which you can accomplish by setting MaxBufferPoolSize=0. You should of course profile your code under different values to determine the settings that will be optimal for your application.