Garnet使用了大量的指针用于优化性能,这里介绍一下LimitedFixedBufferPool:固定缓冲池
1.PoolEntry
/// <summary>
/// Entry
/// </summary>
public readonly byte[] entry;
/// <summary>
/// Entry pointer
/// </summary>
public byte* entryPtr;
/// <summary>
/// Constructor
/// </summary>
public PoolEntry(int size, LimitedFixedBufferPool pool)
{
Debug.Assert(pool != null);
this.pool = pool;
this.disposed = false;
entry = GC.AllocateArray<byte>(size, pinned: true);
entryPtr = (byte*)Unsafe.AsPointer(ref entry[0]);
}
定义:缓冲池中单个缓冲块,包含一个存储数据的字节数组,以及指向 entry 数组第一个元素的指针,在构造函数分配一个指定大小的内存块,并将其与缓冲池关联
2.PoolLevel
/// <summary>
/// Size
/// </summary>
public int size;
/// <summary>
/// Items
/// </summary>
public readonly ConcurrentQueue<PoolEntry> items;
表示缓冲池中的一个级别,每个级别包含一定大小的内存块
3.LimitedFixedBufferPool
固定大小的缓冲池,它管理多个级别的缓冲区,每个级别包含一定数量的预分配的内存块,这个可以有效的减少内存碎片,并降低垃圾回收器的压力,并且使用了Interlocked来确保它的线程安全。
/// <summary>
/// Return
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Return(PoolEntry buffer)
{
int level = Position(buffer.entry.Length);
if (level >= 0)
{
if (pool[level] != null)
{
if (Interlocked.Increment(ref pool[level].size) <= maxEntriesPerLevel)
{
Array.Clear(buffer.entry, 0, buffer.entry.Length);
pool[level].items.Enqueue(buffer);
}
else
Interlocked.Decrement(ref pool[level].size);
}
}
Debug.Assert(totalAllocations > 0, $"Return with {totalAllocations}");
Interlocked.Decrement(ref totalAllocations);
}
/// <summary>
/// Get buffer
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe PoolEntry Get(int size)
{
if (Interlocked.Increment(ref totalAllocations) < 0)
{
Interlocked.Decrement(ref totalAllocations);
logger?.LogError("Invalid Get on disposed pool");
return null;
}
int level = Position(size);
if (level >= 0)
{
if (pool[level] == null)
{
Interlocked.CompareExchange(ref pool[level], new PoolLevel(), null);
}
if (pool[level].items.TryDequeue(out var page))
{
Interlocked.Decrement(ref pool[level].size);
page.Reuse();
return page;
}
}
return new PoolEntry(size, this);
}
主要包括两个方法 Get:顾名思义就是从缓冲池中获取固定大小的PoolEntry,Return:销毁使用完成的缓冲块。
其他方法 Position:其中对大小进行是否为2的幂的检查,这样做能够更好地与操作系统的内存页大小对齐,这有助于减少内存碎片,并可能提高内存访问速度。
使用:这个类在Garnet处理网络请求中被广泛使用。