For the memory release problem, you could offer a manual TrimExcess() or a Trim(int segmentCount) method. What do you think ?
Sébastien On 8/9/06, Sébastien Lorion <[EMAIL PROTECTED]> wrote:
I really like your solution! Making this class a singleton as Yun Jin did seems not like a good choice because different scenarios will most probably require different parameters. Maybe a stupid idea, but I think using a stack would provide better cache locality since it will reuse the last buffer released. Also, the stack Push/Pop methods are faster (about 3 times on my machine). Really just a matter of taste, but personally I would use Request/Release instead of CheckIn/CheckOut. And to be clearer, AvailableBuffers property should be named AvailableBufferCount. Thanks for your code! Sébastien On 8/9/06, gregory young <[EMAIL PROTECTED]> wrote: > After some research this seems like a much better method. Here is the > BufferManager class ... you can use the new overloads for send/receive > that take ArraySegments in conjunction with it. > > Yun Jin gives a great explanation of the problem I am trying to > resolve here https://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx > > I think this is better than the solution presented there for a few reasons. > > 1) the object will be in the LOH (less work for gc as it never deals > with compacting the LOH) > 2) buffers can be dynamically sized .. if you need more buffer just > ask for more array segments (the socket methods take an > ILIst<ArraySegment> > > One area where this suffers is in dealing with memory management (I > cannot easily release memory) but generally in such systems that is > not done often anyways. > > Love to hear some feedback. > > Cheers, > > Greg > > BufferManager.cs > /// <summary> > /// A manager to handle buffers for the socket connections > /// </summary> > /// <remarks> > /// When used in an async call a buffer is pinned. Large numbers > of pinned buffers > /// cause problem with the GC (in particular it causes heap fragmentation). > /// > /// This class maintains a set of large segments and gives clients > pieces of these > /// segments that they can use for their buffers. The alternative > to this would be to > /// create many small arrays which it then maintained. This > methodology should be slightly > /// better than the many small array methodology because in > creating only a few very > /// large objects it will force these objects to be placed on the > LOH. Since the > /// objects are on the LOH they are at this time not subject to > compacting which would > /// require an update of all GC roots as would be the case with > lots of smaller arrays > /// that were in the normal heap. > /// </remarks> > public class BufferManager { > private readonly int m_SegmentChunks; > private readonly int m_ChunkSize; > private readonly int m_SegmentSize; > private readonly Queue<ArraySegment<byte>> m_Buffers; > private readonly object m_LockObject = new Object(); > private readonly List<byte[]> m_Segments; > > /// <summary> > /// The current number of buffers available > /// </summary> > public int AvailableBuffers { > get { return m_Buffers.Count; } //do we really care about > volatility here? > } > > /// <summary> > /// The total size of all buffers > /// </summary> > public int TotalBufferSize { > get { return m_Segments.Count * m_SegmentSize; } //do we > really care about volatility here? > } > > /// <summary> > /// Creates a new segment, makes buffers available > /// </summary> > private void CreateNewSegment() { > byte[] bytes = new byte[m_SegmentChunks * m_ChunkSize]; > m_Segments.Add(bytes); > for (int i = 0; i < m_SegmentChunks; i++) { > ArraySegment<byte> chunk = new > ArraySegment<byte>(bytes, i * m_ChunkSize, m_ChunkSize); > m_Buffers.Enqueue(chunk); > } > } > > /// <summary> > /// Checks out a buffer from the manager > /// </summary> > /// <remarks> > /// It is the client's responsibility to return the buffer to > the manger by > /// calling <see cref="Checkin"></see> on the buffer > /// </remarks> > /// <returns>A <see cref="ArraySegment"></see> that can be > used as a buffer</returns> > public ArraySegment<byte> CheckOut() { > lock (m_LockObject) { > if (m_Buffers.Count == 0) { > CreateNewSegment(); > } > return m_Buffers.Dequeue(); > } > } > > /// <summary> > /// Returns a buffer to the control of the manager > /// </summary> > /// <remarks> > /// It is the client's responsibility to return the buffer to > the manger by > /// calling <see cref="Checkin"></see> on the buffer > /// </remarks> > /// <param name="_Buffer">The <see cref="ArraySegment"></see> > to return to the buffer</param> > public void CheckIn(ArraySegment<byte> _Buffer) { > lock (m_LockObject) { > m_Buffers.Enqueue(_Buffer); > } > } > > #region constructors > > /// <summary> > /// Constructs a new <see cref="BufferManager"></see> object > /// </summary> > /// <param name="_SegmentChunks">The number of chunks to > create per segment</param> > /// <param name="_ChunkSize">The size of a chunk in bytes</param> > public BufferManager(int _SegmentChunks, int _ChunkSize) : > this(_SegmentChunks, _ChunkSize, 1) { } > > /// <summary> > /// Constructs a new <see cref="BufferManager"></see> object > /// </summary> > /// <param name="_SegmentChunks">The number of chunks to > create per segment</param> > /// <param name="_ChunkSize">The size of a chunk in bytes</param> > /// <param name="_InitialSegments">The initial number of > segments to create</param> > public BufferManager(int _SegmentChunks, int _ChunkSize, int > _InitialSegments) { > m_SegmentChunks = _SegmentChunks; > m_ChunkSize = _ChunkSize; > m_SegmentSize = m_SegmentChunks * m_ChunkSize; > m_Buffers = new Queue<ArraySegment<byte>>(_SegmentChunks * > _InitialSegments); > m_Segments = new List<byte[]>(); > for (int i = 0; i < _InitialSegments; i++) { > CreateNewSegment(); > } > } > #endregion > } > > On 8/1/06, gregory young <[EMAIL PROTECTED]> wrote: > > Ok so I think everyone can agree that creating buffers on the fly in > > an async socket server is bad ... there is alot of literature > > available on the problems this will cause with the heap. I am looking > > at a few options to get around this. > > > > 1) Have a BufferPool class that hands out ArraySegment<byte> portions > > of a larger array (large enough that it would be in the LOH). If all > > of the array is used create another big segment. > > > > 2) Create a bunch of smaller arrays for use by the bufferpool class > > and have it hand them back > > > > In both 1 & 2 I would probably have the connection use their buffer > > for the duration of the connection. I would internally hold a list of > > the free blocks. When a connection was done ith its buffer it would > > have to release it back to this pool. My thought is that #2 might be > > better for dealing with cases where I want to shrink the number of > > buffers allocated from the previous maximum if needed. > > > > In general I lean towards #1 ... but figured I would check if I might > > be missing something. > > > > Thanks in advance, > > > > Greg Young > > > > > -- > If knowledge can create problems, it is not through ignorance that we > can solve them. > > Isaac Asimov > > =================================== > This list is hosted by DevelopMentor(r) http://www.develop.com > > View archives and manage your subscription(s) at http://discuss.develop.com > -- Sébastien Lorion Software Architect / Architecte organique [EMAIL PROTECTED]
-- Sébastien Lorion Software Architect / Architecte organique [EMAIL PROTECTED] =================================== This list is hosted by DevelopMentor® http://www.develop.com View archives and manage your subscription(s) at http://discuss.develop.com
