diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs
index 22227fd3..4b0ad74d 100644
--- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs
+++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs
@@ -77,21 +77,14 @@ namespace StgSharp.HighPerformance.Memory
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
}
long remainder = handle->Size - requestedOffset;
- if (remainder > 0)
- {
- Entry* extra = (Entry*)_entries.Allocate();
- extra->Position = handle->Position + (nuint)requestedOffset;
- extra->Size = (uint)remainder;
- extra->State = EntryState.Empty;
- extra->NextNear = handle->NextNear;
- extra->PreviousNear = handle;
- extra->NextNear->PreviousNear = extra;
- extra->PreviousNear->NextNear = extra;
- SliceMemory(extra);
- }
+ SetEntryLevel(handle, handle->Position, (uint)requestedOffset);
handle->State = EntryState.Alloc;
- handle->Size = requestedOffset;
- handle->Level = level;
+ if (remainder > 0) {
+ SliceMemory(handle->NextNear, remainder);
+ }
+ }
+ if (handle->PreviousNear == null || handle->NextNear == null) {
+ throw new HLSFPositionChainBreakException();
}
return new hlsfHandle(handle, size);
}
@@ -103,14 +96,15 @@ namespace StgSharp.HighPerformance.Memory
///
///
///
- private void SliceMemory(Entry* e)
+ ///
+ ///
+ ///
+ private void SliceMemory(Entry* e, long sizeToSlice)
{
- Entry* cur = e->PreviousNear;
- Entry* next = e->NextNear;
- long remain = e->Size;
- long offset = (long)e->Position - (long)m_Buffer;
- long end = offset + remain;
- _entries.Free((nuint)e);
+ Entry* next = e;
+ long remain = sizeToSlice; // remained size to slice
+ long offset = (long)e->Position - sizeToSlice - (long)m_Buffer; // offset of buffer to slice
+ long end = (long)e->Position - (long)m_Buffer; // end offset of buffer to slice
int i = (BitOperations.TrailingZeroCount(remain) - 6) / 2;
i = int.Min(10, i);
@@ -125,21 +119,15 @@ namespace StgSharp.HighPerformance.Memory
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;
+ InsertEntryBefore(newEntry, next);
+ SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty;
- newEntry->Size = (uint)newSize;
- newEntry->Level = i;
BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry;
- PushLevel(i, count - 1, b);
+ PushBucketToLevel(i, count - 1, b);
offset += newSize;
- remain -= (long)newSize;
- cur = newEntry;
+ remain -= newSize;
}
if (remain <= 0) {
return;
@@ -157,19 +145,13 @@ namespace StgSharp.HighPerformance.Memory
int newSize = count * upper;
Entry* newEntry = (Entry*)_entries.Allocate();
- newEntry->PreviousNear = cur;
- newEntry->NextNear = next;
- cur->NextNear = newEntry;
- next->PreviousNear = newEntry;
- newEntry->Position = (nuint)offset + (nuint)m_Buffer;
+ InsertEntryBefore(newEntry, next);
+ SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty;
- newEntry->Size = (uint)newSize;
- newEntry->Level = i;
BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry;
- PushLevel(i, count - 1, b);
- cur = newEntry;
+ PushBucketToLevel(i, count - 1, b);
offset += newSize;
remain -= (long)newSize;
if (remain <= 0) {
@@ -188,22 +170,15 @@ namespace StgSharp.HighPerformance.Memory
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;
+ InsertEntryBefore(newEntry, next);
+ SetEntryLevel(newEntry, (nuint)offset + (nuint)m_Buffer, newSize);
newEntry->State = EntryState.Empty;
- newEntry->Size = (uint)newSize;
- newEntry->Level = i;
BucketNode* b = (BucketNode*)newEntry->Position;
b->EntryRef = newEntry;
b->NextLevel = null;
b->PreviousLevel = null;
- PushLevel(i, count - 1, b);
-
- cur = newEntry;
+ PushBucketToLevel(i, count - 1, b);
offset += newSize;
remain -= (long)newSize;
}
@@ -236,20 +211,15 @@ namespace StgSharp.HighPerformance.Memory
// build new entry
Entry* e = (Entry*)_entries.Allocate();
- e->Position = pos;
- e->Size = size;
- Entry* prev = _spareMemory->PreviousNear;
- e->NextNear = _spareMemory;
- e->PreviousNear = prev;
- prev->NextNear = e;
- _spareMemory->PreviousNear = e;
+ SetEntryLevel(e, pos, size);
+ InsertEntryBefore(e, _spareMemory);
e->State = EntryState.Empty;
- e->Level = GetLevelFromSize(size);
// set fields of spare memory
_spareMemory->Size -= size;
_spareMemory->Position += (nuint)size;
+
handle = e;
return true;
}
diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.BucketNode.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.BucketNode.cs
index 1d4080ef..9546f559 100644
--- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.BucketNode.cs
+++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.BucketNode.cs
@@ -35,7 +35,7 @@ namespace StgSharp.HighPerformance.Memory
public unsafe partial class HybridLayerSegregatedFitAllocator
{
- private void PushLevel(int levelIndex, int sizeIndex, BucketNode* node)
+ private void PushBucketToLevel(int levelIndex, int sizeIndex, BucketNode* node)
{
if (node == null || node->EntryRef == null) {
return;
@@ -68,25 +68,6 @@ 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)
{
@@ -97,7 +78,6 @@ namespace StgSharp.HighPerformance.Memory
return;
}
}
-
BucketNode* previous = node->PreviousLevel;
BucketNode* next = node->NextLevel;
diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Debug.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Debug.cs
index e90dedf8..9b0758a9 100644
--- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Debug.cs
+++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Debug.cs
@@ -62,6 +62,42 @@ namespace StgSharp.HighPerformance.Memory
}
}
+ public void DumpPositionChain(bool singleStep = false, int maxCount = 4096)
+ {
+ lock (_lock)
+ {
+ try
+ {
+ Entry* cur = _spareMemory;
+ int cnt = 0;
+ if (cur != null) {
+ cur = cur->NextNear;
+ }
+ Console.WriteLine(EntryToString(_spareMemory));
+ while (cur != null && cur != _spareMemory && cnt < maxCount)
+ {
+ Console.WriteLine(EntryToString(cur));
+ cnt++;
+ if (singleStep) {
+ Console.ReadKey();
+ }
+
+ cur = cur->NextNear;
+ }
+ if (cur == _spareMemory)
+ {
+ Console.WriteLine("(reached sentinel)");
+ } else if (cnt >= maxCount) {
+ Console.WriteLine("(stopped at maxCount)");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"[DumpPositionChain-forward] Exception: {ex.Message}");
+ }
+ }
+ }
+
public IEnumerable ShowEntryLayout()
{
lock (_lock)
@@ -80,8 +116,8 @@ namespace StgSharp.HighPerformance.Memory
private static string EntryToString(Entry* e)
{
BucketNode* b = (BucketNode*)e->Position;
- return $"{(ulong)e->PreviousNear}<-- Position{(ulong)e} Bucket:{{{(ulong)e->Position},{e->Level},{e->Size}}} -->{(ulong)e->NextNear}";
+ return $"{(ulong)e->PreviousNear}<-prev- Position:{(ulong)e} Bucket:{{{(ulong)e->Position},{e->Level},{e->Size}}} -next->{(ulong)e->NextNear}";
}
}
-}
+}
\ No newline at end of file
diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs
index c736d2ca..0aa5b049 100644
--- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs
+++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs
@@ -49,27 +49,28 @@ namespace StgSharp.HighPerformance.Memory
public void Collect()
{
Entry* cur = _spareMemory->NextNear;
+ long size = 0;
while (cur != null && cur != _spareMemory)
{
+ // cur is not empty, skip
if (cur->State != EntryState.Empty)
{
cur = cur->NextNear;
continue;
}
- RemoveFromBucket(cur->Level, DetermineSegmentIndex(cur->Size, cur->Level), (BucketNode*)cur->Position);
- Entry* next = cur->NextNear;
- while (next is not null &&
- next->State == EntryState.Empty &&
- AssertAdjacentTo(cur, next))
+ // remove cur from bucket
+ do
{
- RemoveFromBucket(next->Level, DetermineSegmentIndex(next->Size, next->Level), (BucketNode*)next->Position);
- cur->Size += next->Size;
- RemoveFromPositionChain(next);
- next = cur->NextNear;
+ size += cur->Size;
+ Entry* next = cur->NextNear;
+ RemoveFromBucket(cur->Level, DetermineSegmentIndex(cur->Size, cur->Level), (BucketNode*)cur->Position);
+ RemoveFromPositionChain(cur);
+ cur = next;
+ } while (cur != null && cur != _spareMemory && cur->State == EntryState.Empty);
+ if (size > 0) {
+ SliceMemory(cur, size);
}
- SliceMemory(cur);
- cur = next;
}
}
@@ -108,7 +109,7 @@ namespace StgSharp.HighPerformance.Memory
int finalSegment = DetermineSegmentIndex(finalSize, finalLevel);
// Push the merged entry back to appropriate bucket
- PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
+ PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
// Console.WriteLine("one block freed");
}
diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Level.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Level.cs
index 79732f74..1ce8c7cf 100644
--- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Level.cs
+++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Level.cs
@@ -60,13 +60,13 @@ namespace StgSharp.HighPerformance.Memory
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private int GetLevelFromSize(long size)
+ private static int GetLevelFromSize(long size)
{
return GetLevelFromSize((uint)size);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private int GetLevelFromSize(uint size)
+ private static int GetLevelFromSize(uint size)
{
/*
* Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216]
diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.SegmentEntry.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.SegmentEntry.cs
index 0ee20926..d19a1bdd 100644
--- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.SegmentEntry.cs
+++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.SegmentEntry.cs
@@ -61,12 +61,32 @@ namespace StgSharp.HighPerformance.Memory
/// Determine segment index based on size and originLevel
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private int DetermineSegmentIndex(long size, int level)
+ private static int DetermineSegmentIndex(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)
+ {
+ Entry* next = position->NextNear;
+ entry->NextNear = next;
+ entry->PreviousNear = position;
+ next->PreviousNear = entry;
+ position->NextNear = entry;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void InsertEntryBefore(Entry* entry, Entry* position)
+ {
+ Entry* prev = position->PreviousNear;
+ entry->NextNear = position;
+ entry->PreviousNear = prev;
+ prev->NextNear = entry;
+ position->PreviousNear = entry;
+ }
+
///
/// Used in Free operations, efficiently remove from buckets and merge adjacent blocks.
/// Current Entry will serve as the final merged block. The Merge will be in lazy
@@ -74,12 +94,52 @@ namespace StgSharp.HighPerformance.Memory
///
private void MergeAndRemoveFromBuckets(Entry* entry)
{
- // TODO 后续提供HLSF.Collect全量整理API,从头遍历邻接块进行彻底的合并
- if (entry == null)
- {
+ if (entry == null) {
return;
}
+ /*
+ Console.WriteLine("============ Merge start =======================");
+ try
+ {
+ Console.WriteLine("forward from entry");
+ Entry* __cur = entry;
+ int __cnt = 0;
+ while (__cur != null && __cur != _spareMemory && __cnt < 512)
+ {
+ Console.WriteLine(EntryToString(__cur));
+ __cur = __cur->NextNear;
+ __cnt++;
+ if (__cur == entry)
+ {
+ Console.WriteLine("(looped back to start)");
+ break;
+ }
+ }
+ Console.WriteLine(EntryToString(__cur));
+ Console.WriteLine("MergeAndRemoveFromBuckets: backward from entry");
+ __cur = entry;
+ __cnt = 0;
+ while (__cur != null && __cur != _spareMemory && __cnt < 512)
+ {
+ Console.WriteLine(EntryToString(__cur));
+ __cur = __cur->PreviousNear;
+ __cnt++;
+ if (__cur == entry)
+ {
+ Console.WriteLine("(looped back to start)");
+ break;
+ }
+ }
+ Console.WriteLine(EntryToString(__cur));
+ }
+ catch { }
+ finally
+ {
+ Console.WriteLine("==========================================");
+ }
+ /**/
+
long size = entry->Size;
int originLevel = entry->Level;
int segment = DetermineSegmentIndex(size, originLevel);
@@ -88,8 +148,8 @@ namespace StgSharp.HighPerformance.Memory
// previous direction merge
Entry* p = entry->PreviousNear;
if (p == null ||
+ p == _spareMemory ||
p->State != EntryState.Empty ||
- !AssertAdjacentTo(entry, p) ||
p->Level != entry->Level ||
!AssertBoundary(p, entry, originLevel))
{
@@ -108,8 +168,8 @@ namespace StgSharp.HighPerformance.Memory
// next direction merge
Entry* n = entry->NextNear;
if (n == null ||
+ n == _spareMemory ||
n->State != EntryState.Empty ||
- !AssertAdjacentTo(entry, n) ||
n->Level != entry->Level ||
!AssertBoundary(n, entry, originLevel))
{
@@ -149,6 +209,14 @@ namespace StgSharp.HighPerformance.Memory
_entries.Free((nuint)entry);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void SetEntryLevel(Entry* entry, nuint position, long size)
+ {
+ entry ->Position = position;
+ entry->Size = size;
+ entry->Level = GetLevelFromSize(size);
+ }
+
///
/// Entry structure - streamlined version (only position and state information) Stored in
/// external SLAB
diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs
index 8d76ff36..a2991b7b 100644
--- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs
+++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs
@@ -37,7 +37,7 @@ namespace StgSharp.HighPerformance.Memory
{
// Bucket system field - directly in main allocator class
- private BucketNode*[] _bucketHeads;
+ private readonly BucketNode*[] _bucketHeads;
// _spareMemory is also the beginning position of allocator
private Entry* _spareMemory;
@@ -64,7 +64,7 @@ namespace StgSharp.HighPerformance.Memory
_spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer;
- _spareMemory->Size = (uint)byteSize;
+ _spareMemory->Size = (long)byteSize;
}
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align)
@@ -85,7 +85,7 @@ namespace StgSharp.HighPerformance.Memory
_spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer;
- _spareMemory->Size = (uint)byteSize;
+ _spareMemory->Size = (long)byteSize;
}
public void Dispose()
diff --git a/StgSharpDebug/HlsfStressTest.cs b/StgSharpDebug/HlsfStressTest.cs
index ac18ab23..6e1450fd 100644
--- a/StgSharpDebug/HlsfStressTest.cs
+++ b/StgSharpDebug/HlsfStressTest.cs
@@ -65,6 +65,7 @@ namespace StgSharp
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
BenchAllocThroughput(allocator, size, touch:false);
allocator.Collect();
+ allocator.DumpPositionChain();
GC.Collect();
GC.WaitForPendingFinalizers();
}