hlsf error is solving
This commit is contained in:
@@ -26,8 +26,10 @@
|
|||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics.Tracing;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using hlsfHandle = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocationHandle;
|
using hlsfHandle = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocationHandle;
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
namespace StgSharp.HighPerformance.Memory
|
||||||
@@ -37,7 +39,15 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
public hlsfHandle Alloc(uint size)
|
public hlsfHandle Alloc(uint size)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfGreaterThan<nuint>(size, (nuint)levelSizeArray[^1] * 2);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
int level = GetLevelFromSize(size);
|
int level = GetLevelFromSize(size);
|
||||||
int segmentIndex = DetermineSegmentIndex(size, level);
|
int segmentIndex = DetermineSegmentIndex(size, level);
|
||||||
|
|
||||||
@@ -63,174 +73,144 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
if (handle != null)
|
if (handle != null)
|
||||||
{
|
{
|
||||||
int requestedOffset = (segmentIndex + 1) * levelSizeArray[level];
|
int requestedOffset = (segmentIndex + 1) * levelSizeArray[level];
|
||||||
if ((ulong)requestedOffset > handle->Size)
|
if (requestedOffset > handle->Size) {
|
||||||
{
|
|
||||||
// spare返回的块不足以切出目标大小,避免进入切片导致下溢/越界
|
|
||||||
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
|
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
|
||||||
}
|
}
|
||||||
Entry* extra = (Entry*)_entries.Allocate();
|
long remainder = handle->Size - requestedOffset;
|
||||||
extra->Position = handle->Position + (nuint)requestedOffset;
|
if (remainder > 0)
|
||||||
extra->Size = handle->Size - (uint)requestedOffset;
|
{
|
||||||
extra->State = EntryState.Empty;
|
Entry* extra = (Entry*)_entries.Allocate();
|
||||||
extra->NextNear = handle->NextNear;
|
extra->Position = handle->Position + (nuint)requestedOffset;
|
||||||
extra->PreviousNear = handle;
|
extra->Size = (uint)remainder;
|
||||||
extra->NextNear->PreviousNear = extra;
|
extra->State = EntryState.Empty;
|
||||||
extra->PreviousNear->NextNear = extra;
|
extra->NextNear = handle->NextNear;
|
||||||
SliceMemory(extra);
|
extra->PreviousNear = handle;
|
||||||
|
extra->NextNear->PreviousNear = extra;
|
||||||
|
extra->PreviousNear->NextNear = extra;
|
||||||
|
SliceMemory(extra);
|
||||||
|
}
|
||||||
|
handle->State = EntryState.Alloc;
|
||||||
|
handle->Size = requestedOffset;
|
||||||
|
handle->Level = level;
|
||||||
}
|
}
|
||||||
handle->State = EntryState.Allocated;
|
|
||||||
handle->Level = level;
|
|
||||||
return new hlsfHandle(handle, size);
|
return new hlsfHandle(handle, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Slice the memory block into smaller blocks and add them to the appropriate buckets
|
||||||
|
/// Notice: this method is not responsible for freeing the original BucketNode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">
|
||||||
|
///
|
||||||
|
/// </param>
|
||||||
private void SliceMemory(Entry* e)
|
private void SliceMemory(Entry* e)
|
||||||
{
|
{
|
||||||
Entry* current = e;
|
Entry* cur = e->PreviousNear;
|
||||||
Entry* next = e->NextNear;
|
Entry* next = e->NextNear;
|
||||||
ulong originOffset = e->Position - (ulong)m_Buffer;
|
long remain = e->Size;
|
||||||
ulong finalOffset = originOffset;
|
long offset = (long)e->Position - (long)m_Buffer;
|
||||||
ulong end = e->Position + e->Size;
|
long end = offset + remain;
|
||||||
ulong endOffset = end - (ulong)m_Buffer;
|
_entries.Free((nuint)e);
|
||||||
ulong remainSize = e->Size;
|
|
||||||
|
|
||||||
|
|
||||||
if (finalOffset >= endOffset || remainSize == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get level of left edge
|
|
||||||
int minLevel = int.Max((BitOperations.TrailingZeroCount(finalOffset) - 6) / 2, 0);
|
|
||||||
int i = minLevel >= levelSizeArray.Length ? levelSizeArray.Length - 1 : minLevel;
|
|
||||||
|
|
||||||
|
int i = (BitOperations.TrailingZeroCount(remain) - 6) / 2;
|
||||||
|
i = int.Min(10, i);
|
||||||
for (; i < levelSizeArray.Length; i++)
|
for (; i < levelSizeArray.Length; i++)
|
||||||
{
|
{
|
||||||
int levelSize = levelSizeArray[i];
|
int size = levelSizeArray[i];
|
||||||
ulong levelLong = (ulong)levelSize;
|
int upper = size * 4;
|
||||||
ulong levelUpper = levelLong * 4;
|
long edge = (offset + upper - 1) / upper * upper;
|
||||||
|
int count = (int)((long.Min(edge, end) - offset) / size);
|
||||||
// smallest board that is larger than finalOffset
|
if (count > 0)
|
||||||
ulong board = (finalOffset + levelUpper - 1) / levelUpper * levelUpper;
|
|
||||||
long span = (long)ulong.Min(board, endOffset) - (long)finalOffset;
|
|
||||||
int blockCount = (span <= 0) ? 0 : (int)(span / (long)levelLong);
|
|
||||||
if (blockCount == 0)
|
|
||||||
{
|
{
|
||||||
continue;
|
int newSize = count * size;
|
||||||
}
|
|
||||||
int s = blockCount * levelSize;
|
|
||||||
|
|
||||||
// create and init new entry
|
|
||||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
|
||||||
newEntry->State = EntryState.Empty;
|
|
||||||
newEntry->Position = (nuint)m_Buffer + (nuint)finalOffset;
|
|
||||||
newEntry->Size = (uint)s;
|
|
||||||
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
|
||||||
bucket->EntryRef = newEntry;
|
|
||||||
newEntry->Level = i;
|
|
||||||
|
|
||||||
// link new entry
|
|
||||||
newEntry->PreviousNear = current;
|
|
||||||
newEntry->NextNear = next;
|
|
||||||
current->NextNear = newEntry;
|
|
||||||
next->PreviousNear = newEntry;
|
|
||||||
|
|
||||||
// set new entry
|
|
||||||
current = newEntry;
|
|
||||||
|
|
||||||
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
|
||||||
|
|
||||||
finalOffset += (ulong)s;
|
|
||||||
remainSize -= (ulong)s;
|
|
||||||
if (remainSize <= (ulong)levelSize)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (remainSize == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i >= 10)
|
|
||||||
{
|
|
||||||
int levelSize = levelSizeArray[^1] * 4;
|
|
||||||
ulong levelLong = (ulong)levelSize;
|
|
||||||
ulong board = endOffset / levelLong * levelLong;
|
|
||||||
long span = (long)board - (long)finalOffset;
|
|
||||||
int blockCount = span > 0 ? (int)(span / (long)levelSize) : 0;
|
|
||||||
if (blockCount > 0)
|
|
||||||
{
|
|
||||||
int s = blockCount * levelSize;
|
|
||||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||||
newEntry->State = EntryState.Empty;
|
newEntry->PreviousNear = cur;
|
||||||
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
|
|
||||||
newEntry->Size = (uint)s;
|
|
||||||
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
|
||||||
bucket->EntryRef = newEntry;
|
|
||||||
newEntry->Level = 10;
|
|
||||||
|
|
||||||
// link new entry
|
|
||||||
newEntry->PreviousNear = current;
|
|
||||||
newEntry->NextNear = next;
|
newEntry->NextNear = next;
|
||||||
current->NextNear = newEntry;
|
cur->NextNear = newEntry;
|
||||||
if (next != null) {
|
next->PreviousNear = newEntry;
|
||||||
next->PreviousNear = newEntry;
|
newEntry->Position = (nuint)offset + (nuint)m_Buffer;
|
||||||
}
|
newEntry->State = EntryState.Empty;
|
||||||
|
newEntry->Size = (uint)newSize;
|
||||||
newEntry->Level = i;
|
newEntry->Level = i;
|
||||||
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
|
||||||
|
|
||||||
finalOffset += (ulong)s;
|
BucketNode* b = (BucketNode*)newEntry->Position;
|
||||||
remainSize -= (ulong)s;
|
b->EntryRef = newEntry;
|
||||||
|
PushLevel(i, count - 1, b);
|
||||||
|
offset += newSize;
|
||||||
|
remain -= (long)newSize;
|
||||||
|
cur = newEntry;
|
||||||
}
|
}
|
||||||
if (remainSize == 0) {
|
if (remain <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i -= 1; i >= 0; i--)
|
if (i > MaxLevel)
|
||||||
{
|
{
|
||||||
int levelSize = levelSizeArray[i];
|
int size = levelSizeArray[^1];
|
||||||
ulong levelLong = (ulong)levelSize;
|
int upper = size * 4;
|
||||||
ulong board = endOffset / levelLong * levelLong;
|
long edge = end / upper * upper;
|
||||||
long span = (long)board - (long)finalOffset;
|
int count = (int)((long.Min(edge, end) - offset) / upper);
|
||||||
int blockCount = span > 0 ? (int)(span / levelSize) : 0;
|
if (count > 0)
|
||||||
if (blockCount <= 0)
|
|
||||||
{
|
{
|
||||||
continue;
|
int newSize = count * upper;
|
||||||
}
|
|
||||||
int s = blockCount * levelSize;
|
|
||||||
System.Diagnostics.Debug.Assert(finalOffset + (ulong)s <= endOffset);
|
|
||||||
|
|
||||||
// create and init new entry
|
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
newEntry->PreviousNear = cur;
|
||||||
newEntry->State = EntryState.Empty;
|
newEntry->NextNear = next;
|
||||||
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
|
cur->NextNear = newEntry;
|
||||||
newEntry->Size = (uint)s;
|
|
||||||
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
|
||||||
bucket->EntryRef = newEntry;
|
|
||||||
|
|
||||||
// link new entry
|
|
||||||
newEntry->PreviousNear = current;
|
|
||||||
newEntry->NextNear = next;
|
|
||||||
current->NextNear = newEntry;
|
|
||||||
if (next != null) {
|
|
||||||
next->PreviousNear = newEntry;
|
next->PreviousNear = newEntry;
|
||||||
}
|
newEntry->Position = (nuint)offset + (nuint)m_Buffer;
|
||||||
|
newEntry->State = EntryState.Empty;
|
||||||
|
newEntry->Size = (uint)newSize;
|
||||||
|
newEntry->Level = i;
|
||||||
|
|
||||||
newEntry->Level = i;
|
BucketNode* b = (BucketNode*)newEntry->Position;
|
||||||
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
b->EntryRef = newEntry;
|
||||||
|
PushLevel(i, count - 1, b);
|
||||||
|
cur = newEntry;
|
||||||
|
offset += newSize;
|
||||||
|
remain -= (long)newSize;
|
||||||
|
if (remain <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set new entry
|
for (i--; i >= 0; i--)
|
||||||
current = newEntry;
|
{
|
||||||
|
int size = levelSizeArray[i];
|
||||||
|
long edge = end / size * size;
|
||||||
|
int count = (int)((long.Min(edge, end) - offset) / size);
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
int newSize = count * size;
|
||||||
|
|
||||||
|
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||||
|
newEntry->PreviousNear = cur;
|
||||||
|
newEntry->NextNear = next;
|
||||||
|
cur->NextNear = newEntry;
|
||||||
|
next->PreviousNear = newEntry;
|
||||||
|
newEntry->Position = (nuint)offset + (nuint)m_Buffer;
|
||||||
|
newEntry->State = EntryState.Empty;
|
||||||
|
newEntry->Size = (uint)newSize;
|
||||||
|
newEntry->Level = i;
|
||||||
|
|
||||||
// update lock and data
|
BucketNode* b = (BucketNode*)newEntry->Position;
|
||||||
finalOffset += (ulong)s;
|
b->EntryRef = newEntry;
|
||||||
remainSize -= (ulong)s;
|
b->NextLevel = null;
|
||||||
if (remainSize == 0)
|
b->PreviousLevel = null;
|
||||||
{
|
PushLevel(i, count - 1, b);
|
||||||
break;
|
|
||||||
|
cur = newEntry;
|
||||||
|
offset += newSize;
|
||||||
|
remain -= (long)newSize;
|
||||||
|
}
|
||||||
|
if (remain <= 0) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RemoveFromPositionChain(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -245,51 +225,32 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
private bool TryAllocateNew16MBBlock(out Entry* handle)
|
private bool TryAllocateNew16MBBlock(out Entry* handle)
|
||||||
{
|
{
|
||||||
nuint pos = _spareMemory->Position;
|
nuint pos = _spareMemory->Position;
|
||||||
nuint bufferEnd = (nuint)m_Buffer + _size;
|
long remain = _spareMemory->Size;
|
||||||
|
if (remain <= 0)
|
||||||
if (pos >= bufferEnd || _spareMemory->Size == 0)
|
|
||||||
{
|
|
||||||
handle = null;
|
|
||||||
return false; // No spare memory available
|
|
||||||
}
|
|
||||||
|
|
||||||
nuint desired = (nuint)levelSizeArray[^1] * 4;
|
|
||||||
nuint remain = bufferEnd - pos;
|
|
||||||
nuint blockSize = desired <= remain ? desired : remain;
|
|
||||||
if (blockSize == 0)
|
|
||||||
{
|
{
|
||||||
handle = null;
|
handle = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
long size = remain > 16 * 1024 * 1024L ? 16 * 1024 * 1024L : remain;
|
||||||
|
|
||||||
nuint newPos = pos + blockSize;
|
|
||||||
|
|
||||||
// Try to allocate new Entry from slab allocator
|
// build new entry
|
||||||
handle = (Entry*)_entries.Allocate();
|
Entry* e = (Entry*)_entries.Allocate();
|
||||||
handle->Position = pos;
|
e->Position = pos;
|
||||||
handle->Size = (uint)blockSize;
|
e->Size = size;
|
||||||
handle->Level = GetLevelFromSize((uint)blockSize);
|
Entry* prev = _spareMemory->PreviousNear;
|
||||||
|
e->NextNear = _spareMemory;
|
||||||
|
e->PreviousNear = prev;
|
||||||
|
prev->NextNear = e;
|
||||||
|
_spareMemory->PreviousNear = e;
|
||||||
|
e->State = EntryState.Empty;
|
||||||
|
e->Level = GetLevelFromSize(size);
|
||||||
|
|
||||||
// Update spare memory position & remaining size
|
// set fields of spare memory
|
||||||
_spareMemory->Position = newPos;
|
_spareMemory->Size -= size;
|
||||||
_spareMemory->Size = (uint)(bufferEnd - newPos);
|
_spareMemory->Position += (nuint)size;
|
||||||
|
|
||||||
BucketNode* b = (BucketNode*)handle->Position;
|
|
||||||
b->EntryRef = handle;
|
|
||||||
|
|
||||||
// update position linked list
|
|
||||||
Entry* previous = _spareMemory->PreviousNear;
|
|
||||||
handle->NextNear = _spareMemory;
|
|
||||||
handle->PreviousNear = previous;
|
|
||||||
_spareMemory->PreviousNear = handle; // Replace Volatile.Write with direct access
|
|
||||||
if (previous != null)
|
|
||||||
{
|
|
||||||
previous->NextNear = handle;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
_positionHead = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
handle = e;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,16 +35,6 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||||
{
|
{
|
||||||
|
|
||||||
#region BucketNode Operations
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static bool IsNullOrEmpty(BucketNode* node)
|
|
||||||
{
|
|
||||||
return node == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private void PushLevel(int levelIndex, int sizeIndex, BucketNode* node)
|
private void PushLevel(int levelIndex, int sizeIndex, BucketNode* node)
|
||||||
{
|
{
|
||||||
if (node == null || node->EntryRef == null) {
|
if (node == null || node->EntryRef == null) {
|
||||||
@@ -52,7 +42,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
|
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||||
if (_bucketHeads[index] == (BucketNode*)null)
|
if (_bucketHeads[index] is null)
|
||||||
{
|
{
|
||||||
_bucketHeads[index] = node;
|
_bucketHeads[index] = node;
|
||||||
node->IsInBucket = true;
|
node->IsInBucket = true;
|
||||||
@@ -78,10 +68,29 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void RemoveFromBucket(BucketNode* node)
|
||||||
|
{
|
||||||
|
BucketNode* previous = node->PreviousLevel;
|
||||||
|
BucketNode* next = node->NextLevel;
|
||||||
|
|
||||||
|
if (previous != null) {
|
||||||
|
previous->NextLevel = next;
|
||||||
|
}
|
||||||
|
if (next != null) {
|
||||||
|
next->PreviousLevel = previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->NextLevel = (BucketNode*)null;
|
||||||
|
node->PreviousLevel = (BucketNode*)null;
|
||||||
|
node->IsInBucket = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
|
private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
|
||||||
{
|
{
|
||||||
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
|
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||||
|
|
||||||
if (_bucketHeads[index] == node)
|
if (_bucketHeads[index] == node)
|
||||||
{
|
{
|
||||||
if (TryPopLevel(levelIndex, sizeIndex, out _)) {
|
if (TryPopLevel(levelIndex, sizeIndex, out _)) {
|
||||||
@@ -107,7 +116,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node)
|
private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node)
|
||||||
{
|
{
|
||||||
int index = (3 * levelIndex) + sizeIndex;
|
Index index = levelIndex > MaxLevel ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||||
|
|
||||||
BucketNode* head = _bucketHeads[index];
|
BucketNode* head = _bucketHeads[index];
|
||||||
|
|
||||||
@@ -139,8 +148,10 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
|
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
|
||||||
{
|
{
|
||||||
Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex;
|
Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||||
if (_bucketHeads[index] == node) {
|
if (_bucketHeads[index] == node)
|
||||||
return TryPopLevel(levelIndex, sizeIndex, out _);
|
{
|
||||||
|
bool ok = TryPopLevel(levelIndex, sizeIndex, out _);
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
BucketNode* previous = node->PreviousLevel;
|
BucketNode* previous = node->PreviousLevel;
|
||||||
@@ -157,7 +168,6 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
node->PreviousLevel = null;
|
node->PreviousLevel = null;
|
||||||
node->IsInBucket = false;
|
node->IsInBucket = false;
|
||||||
node->EntryRef = null;
|
node->EntryRef = null;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
namespace StgSharp.HighPerformance.Memory
|
||||||
{
|
{
|
||||||
|
public sealed class HLSFPositionChainBreakException : Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
public HLSFPositionChainBreakException()
|
||||||
|
: base("Position chain of an HLSF allocator is broken") { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -72,7 +80,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
private static string EntryToString(Entry* e)
|
private static string EntryToString(Entry* e)
|
||||||
{
|
{
|
||||||
BucketNode* b = (BucketNode*)e->Position;
|
BucketNode* b = (BucketNode*)e->Position;
|
||||||
return $"{(ulong)e->PreviousNear}<-- Position{(ulong)e} Bucket:{{{e->Position},{e->Level},{e->Size}}} -->{(ulong)e->NextNear}";
|
return $"{(ulong)e->PreviousNear}<-- Position{(ulong)e} Bucket:{{{(ulong)e->Position},{e->Level},{e->Size}}} -->{(ulong)e->NextNear}";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -45,7 +48,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Collect()
|
public void Collect()
|
||||||
{
|
{
|
||||||
Entry* cur = _positionHead;
|
Entry* cur = _spareMemory->NextNear;
|
||||||
while (cur != null && cur != _spareMemory)
|
while (cur != null && cur != _spareMemory)
|
||||||
{
|
{
|
||||||
if (cur->State != EntryState.Empty)
|
if (cur->State != EntryState.Empty)
|
||||||
@@ -53,36 +56,19 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
cur = cur->NextNear;
|
cur = cur->NextNear;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
RemoveFromBucket(cur->Level, DetermineSegmentIndex(cur->Size, cur->Level), (BucketNode*)cur->Position);
|
||||||
|
Entry* next = cur->NextNear;
|
||||||
|
|
||||||
Entry* runHead = cur;
|
while (next is not null &&
|
||||||
Entry* next = runHead->NextNear;
|
|
||||||
|
|
||||||
// Remove head from bucket if currently registered
|
|
||||||
int headLevel = runHead->Level;
|
|
||||||
int headSeg = DetermineSegmentIndex(runHead->Size, headLevel);
|
|
||||||
TryRemoveFromBucket(headLevel, headSeg, (BucketNode*)runHead->Position);
|
|
||||||
|
|
||||||
while (next != null &&
|
|
||||||
next != _spareMemory &&
|
|
||||||
next->State == EntryState.Empty &&
|
next->State == EntryState.Empty &&
|
||||||
IsAdjacentTo(runHead, next))
|
AssertAdjacentTo(cur, next))
|
||||||
{
|
{
|
||||||
int lvl = next->Level;
|
RemoveFromBucket(next->Level, DetermineSegmentIndex(next->Size, next->Level), (BucketNode*)next->Position);
|
||||||
int seg = DetermineSegmentIndex(next->Size, lvl);
|
cur->Size += next->Size;
|
||||||
TryRemoveFromBucket(lvl, seg, (BucketNode*)next->Position);
|
|
||||||
|
|
||||||
runHead->Size += next->Size;
|
|
||||||
RemoveFromPositionChain(next);
|
RemoveFromPositionChain(next);
|
||||||
next = runHead->NextNear;
|
next = cur->NextNear;
|
||||||
}
|
}
|
||||||
|
SliceMemory(cur);
|
||||||
// Recompute level for merged block and push once
|
|
||||||
runHead->Level = GetLevelFromSize(runHead->Size);
|
|
||||||
int finalLevel = runHead->Level;
|
|
||||||
int finalSeg = DetermineSegmentIndex(runHead->Size, finalLevel);
|
|
||||||
((BucketNode*)runHead->Position)->EntryRef = runHead; // ensure consistency
|
|
||||||
PushLevel(finalLevel, finalSeg, (BucketNode*)runHead->Position);
|
|
||||||
|
|
||||||
cur = next;
|
cur = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +85,13 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
if (e == null) {
|
if (e == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (e->State != EntryState.Allocated) {
|
if (e->State == EntryState.Large)
|
||||||
|
{
|
||||||
|
NativeMemory.AlignedFree((void*)e->Position);
|
||||||
|
_entries.Free((nuint)e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e->State != EntryState.Alloc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,12 +102,15 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
MergeAndRemoveFromBuckets(e);
|
MergeAndRemoveFromBuckets(e);
|
||||||
|
|
||||||
// Calculate final level and segment for the merged block
|
// Calculate final level and segment for the merged block
|
||||||
uint finalSize = e->Size;
|
long finalSize = e->Size;
|
||||||
int finalLevel = GetLevelFromSize(finalSize);
|
int finalLevel = GetLevelFromSize(finalSize);
|
||||||
|
e->Level = finalLevel;
|
||||||
int finalSegment = DetermineSegmentIndex(finalSize, finalLevel);
|
int finalSegment = DetermineSegmentIndex(finalSize, finalLevel);
|
||||||
|
|
||||||
// Push the merged entry back to appropriate bucket
|
// Push the merged entry back to appropriate bucket
|
||||||
PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
|
PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
|
||||||
|
|
||||||
|
// Console.WriteLine("one block freed");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,20 +59,26 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
return int.Min(blockSize, _align);
|
return int.Min(blockSize, _align);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private int GetLevelFromSize(long size)
|
||||||
|
{
|
||||||
|
return GetLevelFromSize((uint)size);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private int GetLevelFromSize(uint size)
|
private int GetLevelFromSize(uint size)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216]
|
* Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216]
|
||||||
* Pattern: 64 * 4^level = 2^6 * 2^(2*level) = 2^(6+2*level)
|
* Pattern: 64 * 4^level = 2^6 * 2^(2*level) = 2^(6+2*level)
|
||||||
*
|
*
|
||||||
* For size S, we need: 2^(6+2*level) >= S
|
* For size S, we need: 2^(6+2*level) >= S
|
||||||
* So: 6+2*level >= log2(S)
|
* So: 6+2*level >= log2(S)
|
||||||
* Therefore: level >= (log2(S) - 6) / 2
|
* Therefore: level >= (log2(S) - 6) / 2
|
||||||
*
|
*
|
||||||
* We use hardware LZCNT instruction via BitOperations.LeadingZeroCount
|
* We use hardware LZCNT instruction via BitOperations.LeadingZeroCount
|
||||||
* log2(S) = 31 - LeadingZeroCount(S) for 32-bit values
|
* log2(S) = 31 - LeadingZeroCount(S) for 32-bit values
|
||||||
*/
|
*/
|
||||||
int log2Size = 31 - BitOperations.LeadingZeroCount(size);
|
int log2Size = 31 - BitOperations.LeadingZeroCount(size);
|
||||||
|
|
||||||
// Calculate level: level = max(0, (log2Size - 6) / 2)
|
// Calculate level: level = max(0, (log2Size - 6) / 2)
|
||||||
|
|||||||
@@ -34,6 +34,17 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if two Entries are adjacent in memory
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
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 ||
|
||||||
|
current->Position + (nuint)current->Size == other->Position;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// check out if a bucket is within the same higher-level boundary as currentOffset
|
/// check out if a bucket is within the same higher-level boundary as currentOffset
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -50,21 +61,10 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
/// Determine segment index based on size and originLevel
|
/// Determine segment index based on size and originLevel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private int DetermineSegmentIndex(uint size, int level)
|
private int DetermineSegmentIndex(long size, int level)
|
||||||
{
|
{
|
||||||
level = int.Min(level, MaxLevel);
|
level = int.Min(level, MaxLevel);
|
||||||
return (int)(size - 1) / levelSizeArray[level];
|
return (int)((size - 1) / levelSizeArray[level]);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if two Entries are adjacent in memory
|
|
||||||
/// </summary>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static bool IsAdjacentTo(Entry* current, Entry* other)
|
|
||||||
{
|
|
||||||
// Check if this immediately follows other
|
|
||||||
return other->Position + other->Size == current->Position ||
|
|
||||||
current->Position + current->Size == other->Position;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -80,19 +80,16 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry* result = entry;
|
long size = entry->Size;
|
||||||
|
|
||||||
uint size = entry->Size;
|
|
||||||
int originLevel = entry->Level;
|
int originLevel = entry->Level;
|
||||||
int segment = DetermineSegmentIndex(size, originLevel);
|
int segment = DetermineSegmentIndex(size, originLevel);
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// previous direction merge
|
// previous direction merge
|
||||||
Entry* p = entry->PreviousNear;
|
Entry* p = entry->PreviousNear;
|
||||||
if (p == null ||
|
if (p == null ||
|
||||||
p->State != EntryState.Empty ||
|
p->State != EntryState.Empty ||
|
||||||
!IsAdjacentTo(result, p) ||
|
!AssertAdjacentTo(entry, p) ||
|
||||||
p->Level != entry->Level ||
|
p->Level != entry->Level ||
|
||||||
!AssertBoundary(p, entry, originLevel))
|
!AssertBoundary(p, entry, originLevel))
|
||||||
{
|
{
|
||||||
@@ -112,7 +109,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
Entry* n = entry->NextNear;
|
Entry* n = entry->NextNear;
|
||||||
if (n == null ||
|
if (n == null ||
|
||||||
n->State != EntryState.Empty ||
|
n->State != EntryState.Empty ||
|
||||||
!IsAdjacentTo(result, n) ||
|
!AssertAdjacentTo(entry, n) ||
|
||||||
n->Level != entry->Level ||
|
n->Level != entry->Level ||
|
||||||
!AssertBoundary(n, entry, originLevel))
|
!AssertBoundary(n, entry, originLevel))
|
||||||
{
|
{
|
||||||
@@ -133,24 +130,16 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove node from position linked list
|
/// Remove node from position linked list, this method does not recycle a BucketNode from
|
||||||
|
/// size bucket
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void RemoveFromPositionChain(Entry* entry)
|
private void RemoveFromPositionChain(Entry* entry)
|
||||||
{
|
{
|
||||||
Entry* prev = entry->PreviousNear;
|
Entry* prev = entry->PreviousNear;
|
||||||
Entry* next = entry->NextNear;
|
Entry* next = entry->NextNear;
|
||||||
|
|
||||||
if (prev != null)
|
prev->NextNear = next;
|
||||||
{
|
next->PreviousNear = prev;
|
||||||
prev->NextNear = entry->NextNear;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
_positionHead = entry->NextNear;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next != null) {
|
|
||||||
next->PreviousNear = entry->PreviousNear;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear node pointers
|
// Clear node pointers
|
||||||
entry->NextNear = null;
|
entry->NextNear = null;
|
||||||
@@ -164,25 +153,28 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
/// Entry structure - streamlined version (only position and state information) Stored in
|
/// Entry structure - streamlined version (only position and state information) Stored in
|
||||||
/// external SLAB
|
/// external SLAB
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 36)]
|
[StructLayout(LayoutKind.Explicit, Size = 40)]
|
||||||
internal struct Entry
|
internal struct Entry
|
||||||
{
|
{
|
||||||
|
|
||||||
[FieldOffset(8)] internal Entry* NextNear; // Position linked list: next pointer
|
[FieldOffset(0)] internal Entry* NextNear; // Position linked list: next pointer
|
||||||
[FieldOffset(16)] internal Entry* PreviousNear; // Position linked list: previous pointer
|
[FieldOffset(8)] internal Entry* PreviousNear; // Position linked list: previous pointer
|
||||||
|
|
||||||
[FieldOffset(28)] internal int _Level; // Level of the block in the allocator
|
[FieldOffset(24)] internal int _Level; // Level of the block in the allocator
|
||||||
[FieldOffset(24)] internal uint Size; // Block size information
|
[FieldOffset(16)] internal long Size; // Block size information
|
||||||
[FieldOffset(32)] public EntryState State;
|
[FieldOffset(36)] public EntryState State;
|
||||||
[FieldOffset(0)] public nuint Position; // Memory block position
|
[FieldOffset(28)] public nuint Position; // Memory block position
|
||||||
|
|
||||||
public int Level
|
public int Level
|
||||||
{
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get => _Level;
|
get => _Level;
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value > 15 || value < 0) {
|
int size = 64 * (1 << (value * 2));
|
||||||
throw new InvalidOperationException();
|
if (Size / size > 3 && Size < 64 * 1024 * 1024L) {
|
||||||
|
throw new HLSFPositionChainBreakException();
|
||||||
}
|
}
|
||||||
_Level = value;
|
_Level = value;
|
||||||
}
|
}
|
||||||
@@ -195,7 +187,8 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
Spare,
|
Spare,
|
||||||
Empty,
|
Empty,
|
||||||
Allocated
|
Alloc,
|
||||||
|
Large
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,9 +39,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
// Bucket system field - directly in main allocator class
|
// Bucket system field - directly in main allocator class
|
||||||
private BucketNode*[] _bucketHeads;
|
private BucketNode*[] _bucketHeads;
|
||||||
|
|
||||||
// Head of the position linked list (first Entry in memory order)
|
// _spareMemory is also the beginning position of allocator
|
||||||
private Entry* _positionHead;
|
|
||||||
|
|
||||||
private Entry* _spareMemory;
|
private Entry* _spareMemory;
|
||||||
private readonly byte* m_Buffer;
|
private readonly byte* m_Buffer;
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
@@ -63,8 +61,8 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
_spareMemory = (Entry*)_entries.Allocate();
|
_spareMemory = (Entry*)_entries.Allocate();
|
||||||
_spareMemory->State = EntryState.Spare;
|
_spareMemory->State = EntryState.Spare;
|
||||||
_spareMemory->PreviousNear = null;
|
_spareMemory->PreviousNear = _spareMemory;
|
||||||
_spareMemory->NextNear = null;
|
_spareMemory->NextNear = _spareMemory;
|
||||||
_spareMemory->Position = (nuint)m_Buffer;
|
_spareMemory->Position = (nuint)m_Buffer;
|
||||||
_spareMemory->Size = (uint)byteSize;
|
_spareMemory->Size = (uint)byteSize;
|
||||||
}
|
}
|
||||||
@@ -73,19 +71,19 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
{
|
{
|
||||||
if (align < 16 || (align & (align - 1)) != 0) {
|
if (align < 16 || (align & (align - 1)) != 0) {
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
"Alignment must be a power of two and at least 8 bytes.");
|
"Alignment must be a power of two and at least 16 bytes.");
|
||||||
}
|
}
|
||||||
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked);
|
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked);
|
||||||
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
|
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
|
||||||
_align = align;
|
_align = align;
|
||||||
_size = byteSize;
|
_size = byteSize;
|
||||||
|
|
||||||
_bucketHeads = new BucketNode*[levelSizeArray.Length * 3];
|
_bucketHeads = new BucketNode*[levelSizeArray.Length * 3 + 1];
|
||||||
|
|
||||||
_spareMemory = (Entry*)_entries.Allocate();
|
_spareMemory = (Entry*)_entries.Allocate();
|
||||||
_spareMemory->State = EntryState.Empty;
|
_spareMemory->State = EntryState.Empty;
|
||||||
_spareMemory->PreviousNear = null;
|
_spareMemory->PreviousNear = _spareMemory;
|
||||||
_spareMemory->NextNear = null;
|
_spareMemory->NextNear = _spareMemory;
|
||||||
_spareMemory->Position = (nuint)m_Buffer;
|
_spareMemory->Position = (nuint)m_Buffer;
|
||||||
_spareMemory->Size = (uint)byteSize;
|
_spareMemory->Size = (uint)byteSize;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
"profiles": {
|
"profiles": {
|
||||||
"StgSharpDebug": {
|
"StgSharpDebug": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"nativeDebugging": true
|
"nativeDebugging": true,
|
||||||
|
"hotReloadEnabled": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user