HLSf can now set max block size, not only 16MB
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
//-----------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Alloc"
|
||||
// Project: StgSharp
|
||||
@@ -37,19 +37,16 @@ namespace StgSharp.HighPerformance.Memory
|
||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||
{
|
||||
|
||||
public hlsfHandle Alloc(uint size)
|
||||
public hlsfHandle Alloc(
|
||||
uint size
|
||||
)
|
||||
{
|
||||
if (size > 32U * 1024U * 1024U)
|
||||
{
|
||||
Entry* e = (Entry*)_entries.Allocate();
|
||||
e->Size = size;
|
||||
e->Position = (nuint)NativeMemory.AlignedAlloc(size, (nuint)_align);
|
||||
e->State = EntryState.Large;
|
||||
return new hlsfHandle(e, size);
|
||||
if (size > _maxSize) {
|
||||
throw new InvalidOperationException("Requested size exceeds maximum allocatable size.");
|
||||
}
|
||||
|
||||
int level = GetLevelFromSize(size);
|
||||
int segmentIndex = DetermineSegmentIndex(size, level);
|
||||
int segmentIndex = DetermineSegmentIndex(_levelSizeArray, size, level);
|
||||
|
||||
int i = level, s = segmentIndex;
|
||||
Entry* handle;
|
||||
@@ -75,7 +72,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
|
||||
if (handle != null)
|
||||
{
|
||||
int requestedOffset = (segmentIndex + 1) * levelSizeArray[level];
|
||||
int requestedOffset = (segmentIndex + 1) * _levelSizeArray[level];
|
||||
if (requestedOffset > handle->Size) {
|
||||
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
|
||||
}
|
||||
@@ -99,7 +96,10 @@ namespace StgSharp.HighPerformance.Memory
|
||||
/// <param name="sizeToSlice">
|
||||
///
|
||||
/// </param>
|
||||
private void SliceMemory(Entry* e, long sizeToSlice)
|
||||
private void SliceMemory(
|
||||
Entry* e,
|
||||
long sizeToSlice
|
||||
)
|
||||
{
|
||||
Entry* next = e;
|
||||
long remain = sizeToSlice; // remained size to slice
|
||||
@@ -108,9 +108,9 @@ namespace StgSharp.HighPerformance.Memory
|
||||
|
||||
int i = (BitOperations.TrailingZeroCount(offset) - 6) / 2;
|
||||
i = int.Min(10, i);
|
||||
for (; i < levelSizeArray.Length; i++)
|
||||
for (; i < _levelSizeArray.Length; i++)
|
||||
{
|
||||
int size = levelSizeArray[i];
|
||||
int size = _levelSizeArray[i];
|
||||
int upper = size * 4;
|
||||
long edge = (offset + upper - 1) / upper * upper;
|
||||
int count = (int)((long.Min(edge, end) - offset) / size);
|
||||
@@ -136,7 +136,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
|
||||
if (i > MaxLevel)
|
||||
{
|
||||
int size = levelSizeArray[^1];
|
||||
int size = _levelSizeArray[^1];
|
||||
int upper = size * 4;
|
||||
long edge = end / upper * upper;
|
||||
int count = (int)((long.Min(edge, end) - offset) / upper);
|
||||
@@ -162,7 +162,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
|
||||
for (i--; i >= 0; i--)
|
||||
{
|
||||
int size = levelSizeArray[i];
|
||||
int size = _levelSizeArray[i];
|
||||
long edge = end / size * size;
|
||||
int count = (int)((long.Min(edge, end) - offset) / size);
|
||||
if (count > 0)
|
||||
@@ -197,7 +197,9 @@ namespace StgSharp.HighPerformance.Memory
|
||||
/// <returns>
|
||||
/// True if allocation succeeded
|
||||
/// </returns>
|
||||
private bool TryAllocateNew32MBBlock(out Entry* handle)
|
||||
private bool TryAllocateNew32MBBlock(
|
||||
out Entry* handle
|
||||
)
|
||||
{
|
||||
nuint pos = _spareMemory->Position;
|
||||
long remain = _spareMemory->Size;
|
||||
@@ -227,7 +229,11 @@ namespace StgSharp.HighPerformance.Memory
|
||||
/// <summary>
|
||||
/// Helper method for allocation - converts BucketNode to Entry
|
||||
/// </summary>
|
||||
private bool TryPopLevelForAllocation(int levelIndex, int sizeIndex, out Entry* entry)
|
||||
private bool TryPopLevelForAllocation(
|
||||
int levelIndex,
|
||||
int sizeIndex,
|
||||
out Entry* entry
|
||||
)
|
||||
{
|
||||
if (TryPopLevel(levelIndex, sizeIndex, out BucketNode* bucketNode))
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//-----------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Free"
|
||||
// Project: StgSharp
|
||||
@@ -64,7 +64,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
{
|
||||
size += cur->Size;
|
||||
Entry* next = cur->NextNear;
|
||||
RemoveFromBucket(cur->Level, DetermineSegmentIndex(cur->Size, cur->Level), (BucketNode*)cur->Position);
|
||||
RemoveFromBucket(cur->Level, DetermineSegmentIndex(_levelSizeArray, cur->Size, cur->Level), (BucketNode*)cur->Position);
|
||||
RemoveFromPositionChain(cur);
|
||||
cur = next;
|
||||
} while (cur != null && cur != _spareMemory && cur->State == EntryState.Empty);
|
||||
@@ -80,18 +80,14 @@ namespace StgSharp.HighPerformance.Memory
|
||||
/// <param name="handle">
|
||||
/// Handle to the allocated memory block
|
||||
/// </param>
|
||||
public void Free(hlsfHandle handle)
|
||||
public void Free(
|
||||
hlsfHandle handle
|
||||
)
|
||||
{
|
||||
Entry* e = handle.EntryHandle;
|
||||
if (e == null) {
|
||||
return;
|
||||
}
|
||||
if (e->State == EntryState.Large)
|
||||
{
|
||||
NativeMemory.AlignedFree((void*)e->Position);
|
||||
_entries.Free((nuint)e);
|
||||
return;
|
||||
}
|
||||
if (e->State != EntryState.Alloc) {
|
||||
return;
|
||||
}
|
||||
@@ -106,7 +102,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
long finalSize = e->Size;
|
||||
int finalLevel = GetLevelFromSize(finalSize);
|
||||
e->Level = finalLevel;
|
||||
int finalSegment = DetermineSegmentIndex(finalSize, finalLevel);
|
||||
int finalSegment = DetermineSegmentIndex(_levelSizeArray, finalSize, finalLevel);
|
||||
|
||||
// Push the merged entry back to appropriate bucket
|
||||
PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//-----------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.Level"
|
||||
// Project: StgSharp
|
||||
@@ -40,7 +40,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
|
||||
private const int MaxLevel = 9;
|
||||
|
||||
private static readonly int[] levelSizeArray = [
|
||||
private int[] _levelSizeArray = [
|
||||
64,
|
||||
256,
|
||||
1024,
|
||||
@@ -53,20 +53,26 @@ namespace StgSharp.HighPerformance.Memory
|
||||
16777216
|
||||
];
|
||||
|
||||
public int AlignOfLevel(int bucketLevel)
|
||||
public int AlignOfLevel(
|
||||
int bucketLevel
|
||||
)
|
||||
{
|
||||
int blockSize = levelSizeArray[bucketLevel];
|
||||
int blockSize = _levelSizeArray[bucketLevel];
|
||||
return int.Min(blockSize, _align);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetLevelFromSize(long size)
|
||||
private static int GetLevelFromSize(
|
||||
long size
|
||||
)
|
||||
{
|
||||
return GetLevelFromSize((uint)size);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetLevelFromSize(uint size)
|
||||
private static int GetLevelFromSize(
|
||||
uint size
|
||||
)
|
||||
{
|
||||
/*
|
||||
* Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//-----------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator.SegmentEntry"
|
||||
// Project: StgSharp
|
||||
@@ -38,7 +38,10 @@ namespace StgSharp.HighPerformance.Memory
|
||||
/// Check if two Entries are adjacent in memory
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool AssertAdjacentTo(Entry* current, Entry* other)
|
||||
private static bool AssertAdjacentTo(
|
||||
Entry* current,
|
||||
Entry* other
|
||||
)
|
||||
{
|
||||
// Only a predicate; never throw here. Callers rely on false to skip merging.
|
||||
return other->Position + (nuint)other->Size == current->Position ||
|
||||
@@ -48,10 +51,14 @@ namespace StgSharp.HighPerformance.Memory
|
||||
/// <summary>
|
||||
/// check out if a bucket is within the same higher-level boundary as currentOffset
|
||||
/// </summary>
|
||||
private bool AssertBoundary(Entry* previous, Entry* current, int level)
|
||||
private bool AssertBoundary(
|
||||
Entry* previous,
|
||||
Entry* current,
|
||||
int level
|
||||
)
|
||||
{
|
||||
level = int.Min(level, MaxLevel);
|
||||
int nextLevelSize = levelSizeArray[level] * 4;
|
||||
int nextLevelSize = _levelSizeArray[level] * 4;
|
||||
ulong prevHead = previous->Position - (ulong)m_Buffer,
|
||||
currHead = current->Position - (ulong)m_Buffer;
|
||||
return (currHead / (ulong)nextLevelSize) == (prevHead / (ulong)nextLevelSize);
|
||||
@@ -61,14 +68,21 @@ namespace StgSharp.HighPerformance.Memory
|
||||
/// Determine segment index based on size and originLevel
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int DetermineSegmentIndex(long size, int level)
|
||||
private static int DetermineSegmentIndex(
|
||||
int[] levelSizeArray,
|
||||
long size,
|
||||
int level
|
||||
)
|
||||
{
|
||||
level = int.Min(level, MaxLevel);
|
||||
return (int)((size - 1) / levelSizeArray[level]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InsertEntryAfter(Entry* entry, Entry* position)
|
||||
private static void InsertEntryAfter(
|
||||
Entry* entry,
|
||||
Entry* position
|
||||
)
|
||||
{
|
||||
Entry* next = position->NextNear;
|
||||
entry->NextNear = next;
|
||||
@@ -78,7 +92,10 @@ namespace StgSharp.HighPerformance.Memory
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InsertEntryBefore(Entry* entry, Entry* position)
|
||||
private static void InsertEntryBefore(
|
||||
Entry* entry,
|
||||
Entry* position
|
||||
)
|
||||
{
|
||||
Entry* prev = position->PreviousNear;
|
||||
entry->NextNear = position;
|
||||
@@ -92,7 +109,9 @@ namespace StgSharp.HighPerformance.Memory
|
||||
/// Current Entry will serve as the final merged block. The Merge will be in lazy
|
||||
/// strategy.
|
||||
/// </summary>
|
||||
private void MergeAndRemoveFromBuckets(Entry* entry)
|
||||
private void MergeAndRemoveFromBuckets(
|
||||
Entry* entry
|
||||
)
|
||||
{
|
||||
if (entry == null) {
|
||||
return;
|
||||
@@ -101,7 +120,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
|
||||
long size = entry->Size;
|
||||
int originLevel = entry->Level;
|
||||
int segment = DetermineSegmentIndex(size, originLevel);
|
||||
int segment = DetermineSegmentIndex(_levelSizeArray, size, originLevel);
|
||||
do
|
||||
{
|
||||
// previous direction merge
|
||||
@@ -118,7 +137,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
// Console.WriteLine($"{(ulong)p->Size} merged to {(ulong)entry->Size} previous");
|
||||
entry->Size += p->Size;
|
||||
entry->Position = p->Position;
|
||||
RemoveFromBucket(originLevel, DetermineSegmentIndex(p->Size, originLevel), (BucketNode*)p->Position);
|
||||
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, p->Size, originLevel), (BucketNode*)p->Position);
|
||||
RemoveFromPositionChain(p);
|
||||
} while (true);
|
||||
|
||||
@@ -137,11 +156,11 @@ namespace StgSharp.HighPerformance.Memory
|
||||
|
||||
// Console.WriteLine($"{(ulong)n->Size} merged to {(ulong)entry->Size} next");
|
||||
entry->Size += n->Size;
|
||||
RemoveFromBucket(originLevel, DetermineSegmentIndex(n->Size, originLevel), (BucketNode*)n->Position);
|
||||
RemoveFromBucket(originLevel, DetermineSegmentIndex(_levelSizeArray, n->Size, originLevel), (BucketNode*)n->Position);
|
||||
RemoveFromPositionChain(n);
|
||||
} while (true);
|
||||
originLevel = int.Min(9, originLevel);
|
||||
entry->Level += entry->Size >= levelSizeArray[originLevel] * 4 ? 1 : 0;
|
||||
entry->Level += entry->Size >= _levelSizeArray[originLevel] * 4 ? 1 : 0;
|
||||
entry->Level &= 15;
|
||||
|
||||
BucketNode* bucket = (BucketNode*)entry->Position;
|
||||
@@ -152,7 +171,9 @@ namespace StgSharp.HighPerformance.Memory
|
||||
/// Remove node from position linked list, this method does not recycle a BucketNode from
|
||||
/// size bucket
|
||||
/// </summary>
|
||||
private void RemoveFromPositionChain(Entry* entry)
|
||||
private void RemoveFromPositionChain(
|
||||
Entry* entry
|
||||
)
|
||||
{
|
||||
Entry* prev = entry->PreviousNear;
|
||||
Entry* next = entry->NextNear;
|
||||
@@ -169,7 +190,11 @@ namespace StgSharp.HighPerformance.Memory
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void SetEntryLevel(Entry* entry, nuint position, long size)
|
||||
private static void SetEntryLevel(
|
||||
Entry* entry,
|
||||
nuint position,
|
||||
long size
|
||||
)
|
||||
{
|
||||
entry ->Position = position;
|
||||
entry->Size = size;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//-----------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------
|
||||
// file="HLSFAllocator"
|
||||
// Project: StgSharp
|
||||
@@ -37,55 +37,130 @@ namespace StgSharp.HighPerformance.Memory
|
||||
{
|
||||
|
||||
// Bucket system field - directly in main allocator class
|
||||
private readonly BucketNode*[] _bucketHeads;
|
||||
private readonly BucketNode *[] _bucketHeads;
|
||||
|
||||
// _spareMemory is also the beginning position of allocator
|
||||
private Entry* _spareMemory;
|
||||
private readonly byte* m_Buffer;
|
||||
private Entry *_spareMemory;
|
||||
private readonly byte *m_Buffer;
|
||||
private bool disposedValue;
|
||||
private readonly int _align;
|
||||
private readonly nuint _size;
|
||||
private readonly SlabAllocator<Entry> _entries;
|
||||
private readonly uint _maxSize;
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(nuint byteSize)
|
||||
public HybridLayerSegregatedFitAllocator(
|
||||
nuint byteSize
|
||||
)
|
||||
{
|
||||
if (byteSize < 64) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
|
||||
_size = byteSize;
|
||||
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, 16);
|
||||
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, 16);
|
||||
_align = 16;
|
||||
|
||||
_bucketHeads = new BucketNode*[levelSizeArray.Length * 3 + 1];
|
||||
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
|
||||
|
||||
_spareMemory = (Entry*)_entries.Allocate();
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Spare;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
_maxSize = 32U * 1024U * 1024U;
|
||||
}
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align)
|
||||
public HybridLayerSegregatedFitAllocator(
|
||||
nuint byteSize,
|
||||
uint maxBlockSize
|
||||
)
|
||||
{
|
||||
if (align < 16 || (align & (align - 1)) != 0) {
|
||||
throw new ArgumentException(
|
||||
"Alignment must be a power of two and at least 16 bytes.");
|
||||
}
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked);
|
||||
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
|
||||
_align = align;
|
||||
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)16);
|
||||
_align = 16;
|
||||
_size = byteSize;
|
||||
|
||||
_bucketHeads = new BucketNode*[levelSizeArray.Length * 3 + 1];
|
||||
|
||||
_spareMemory = (Entry*)_entries.Allocate();
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Empty;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
int levelCountOrigin = _levelSizeArray.Length;
|
||||
int levelCount = GetLevelFromSize(maxBlockSize) + 1;
|
||||
Array.Resize(ref _levelSizeArray, levelCount);
|
||||
_bucketHeads = new BucketNode *[levelCount * 3 + 1];
|
||||
int size = _levelSizeArray[^1];
|
||||
for (int i = levelCountOrigin; i < levelCount; i++)
|
||||
{
|
||||
size *= 4;
|
||||
_levelSizeArray[i] = size;
|
||||
}
|
||||
_maxSize = maxBlockSize;
|
||||
}
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(
|
||||
nuint byteSize,
|
||||
int align
|
||||
)
|
||||
{
|
||||
if (align < 16 || (align & (align - 1)) != 0) {
|
||||
throw new ArgumentException(
|
||||
"Alignment must be a power of two and at least 16 bytes.");
|
||||
}
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked);
|
||||
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
|
||||
_align = align;
|
||||
_size = byteSize;
|
||||
|
||||
_bucketHeads = new BucketNode *[_levelSizeArray.Length * 3 + 1];
|
||||
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Empty;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
_maxSize = 32U * 1024U * 1024U;
|
||||
}
|
||||
|
||||
public HybridLayerSegregatedFitAllocator(
|
||||
nuint byteSize,
|
||||
int align,
|
||||
uint maxBlockSize
|
||||
)
|
||||
{
|
||||
if (align < 16 || (align & (align - 1)) != 0) {
|
||||
throw new ArgumentException(
|
||||
"Alignment must be a power of two and at least 16 bytes.");
|
||||
}
|
||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked);
|
||||
m_Buffer = (byte *)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
|
||||
_align = align;
|
||||
_size = byteSize;
|
||||
|
||||
_spareMemory = (Entry *)_entries.Allocate();
|
||||
_spareMemory->State = EntryState.Empty;
|
||||
_spareMemory->PreviousNear = _spareMemory;
|
||||
_spareMemory->NextNear = _spareMemory;
|
||||
_spareMemory->Position = (nuint)m_Buffer;
|
||||
_spareMemory->Size = (long)byteSize;
|
||||
|
||||
int levelCountOrigin = _levelSizeArray.Length;
|
||||
int levelCount = GetLevelFromSize(maxBlockSize) + 1;
|
||||
Array.Resize(ref _levelSizeArray, levelCount);
|
||||
_bucketHeads = new BucketNode *[levelCount * 3 + 1];
|
||||
int size = _levelSizeArray[^1];
|
||||
for (int i = levelCountOrigin; i < levelCount; i++)
|
||||
{
|
||||
size *= 4;
|
||||
_levelSizeArray[i] = size;
|
||||
}
|
||||
_maxSize = maxBlockSize;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -94,7 +169,9 @@ namespace StgSharp.HighPerformance.Memory
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
protected virtual void Dispose(
|
||||
bool disposing
|
||||
)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user