From 482cddd6bfc4bbb1cc03d93066f8a39c2f0b229d Mon Sep 17 00:00:00 2001 From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:01:34 +0800 Subject: [PATCH] hlsf error is solving --- .../Memory/HLSFAllocator.Alloc.cs | 86 ++++++------------- .../Memory/HLSFAllocator.BucketNode.cs | 22 +---- .../Memory/HLSFAllocator.Debug.cs | 40 ++++++++- .../Memory/HLSFAllocator.Free.cs | 25 +++--- .../Memory/HLSFAllocator.Level.cs | 4 +- .../Memory/HLSFAllocator.SegmentEntry.cs | 80 +++++++++++++++-- .../HighPerformance/Memory/HLSFAllocator.cs | 6 +- StgSharpDebug/HlsfStressTest.cs | 1 + 8 files changed, 160 insertions(+), 104 deletions(-) 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(); }