HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

A blocking buffer manager to provide segments of a byte array

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
providearraybyteblockingmanagersegmentsbuffer

Problem

Since asynchronous operations (like Socket's Begin-End pairs and *Async methods) that use IOCP under the hood cause the byte array that you use as buffer to be pinned in the memory.

So if you create a new byte array everytime you send to, or receive from a socket using IOCP, you're likely to encounter OutOfMemoryExceptions in your program.

One workaround for this is to pool the buffers, and a better one is to create one big byte array and use its parts as buffers. I first saw an implementation of this approach here and I wanted to wrote a simpler one to learn and use in my own projects:

```
public sealed class BufferManager : IDisposable
{
// Fields
private readonly byte[] _Block;
private volatile bool _Disposed;
private readonly BlockingCollection _FreeSegments;
private readonly int _SegmentSize;

// Properties
public byte[] Block { get { return _Block; } }
public int BlockSize { get { return _Block.Length; } }
public int BufferSize { get { return _SegmentSize; } }
public int FreeBuffers { get { return _FreeSegments.Count; } }

// Constructors
public BufferManager(int blockSize, int bufferSize)
{
// These are some helper extensions that take minimum and maximum values.
blockSize.ThrowIfOutOfRange(1, paramName: "blockSize");
bufferSize.ThrowIfOutOfRange(1, blockSize, "bufferSize");

// Setting the block size,
// So all the bytes will be available according to buffer size.
var mod = blockSize % bufferSize;
if (mod != 0)
blockSize = (blockSize - mod) + bufferSize;

// Determining the first byte's index in each segment.
var freeSegments = new int[blockSize / bufferSize];
var freeSegment = 0;
for (int i = 0; i
(
// BlockingCollection uses ConcurrentQueue as default underlaying...
// ...collection but since the order is not important, I thought it can...
// ...be

Solution

-
var mod = blockSize % bufferSize;
if (mod != 0)
    blockSize = (blockSize - mod) + bufferSize;


Wouldn't it be better if the constructor actually accepted bufferCount instead of blockSize, so that you wouldn't have to have this non-obvious logic? Or maybe just throw an exception if blockSize is not divisible by bufferSize.

-

I thought it can be faster with a ConcurrentBag.

Are you sure about that? ConcurrentBag is optimized for the situation when a thread consumes what it produces, I'm not sure that's the situation you're likely to be in (though you could be). If you're changing the default for performance reasons, you should first make sure it actually makes a difference (you're dealing with IO, that's most likely going to dominate by a lot). And if it does make a difference, you should measure that your version is actually faster, not just make guesses.

-
Consider making GetOffset() and FreeOffset() private. Consumers of your class shouldn't know about its implementation (single block with equally sized buffers). GetBuffer() and FreeBuffer() provide a much better abstraction, and ArraySegment is a struct, so it shouldn't cause a noticeable performance penalty.

-
FreeBuffer() should check that the buffer it receives is valid: that it refers to the block (and not some other array) and that it has correct length and offset. The same applies to FreeOffset(), if you decide to keep it public.

Code Snippets

var mod = blockSize % bufferSize;
if (mod != 0)
    blockSize = (blockSize - mod) + bufferSize;

Context

StackExchange Code Review Q#17804, answer score: 4

Revisions (0)

No revisions yet.