From 11b630ffa272126ee707ee395e42fbd15d285614 Mon Sep 17 00:00:00 2001 From: Youmu Liu <105042599+Nitload-NSI@users.noreply.github.com> Date: Sat, 23 Aug 2025 22:05:47 +0800 Subject: [PATCH] reset hlsf to single thread --- .../Memory/HLSFAllocator.Alloc.cs | 276 ++++------ .../Memory/HLSFAllocator.BucketNode.cs | 491 +++--------------- .../Memory/HLSFAllocator.Free.cs | 49 +- .../Memory/HLSFAllocator.SegmentEntry.cs | 386 ++++---------- .../HighPerformance/Memory/HLSFAllocator.cs | 4 - StgSharpDebug/Program.cs | 173 ++++-- 6 files changed, 441 insertions(+), 938 deletions(-) diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs index a6622d1e..986758ce 100644 --- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs +++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs @@ -82,23 +82,13 @@ namespace StgSharp.HighPerformance.Memory } } - try + if (handle != null) { - if (handle != null) - { - AcquireEntryNextLock(handle); - uint allocSize = (uint)((segmentIndex + 1) * levelSizeArray[level]); - SliceMemory(handle, (segmentIndex + 1) * levelSizeArray[level]); - handle->Level = level; - handle->Size = allocSize; - SetEntryState(handle, EntryState.Allocated); - } - return new hlsfHandle(handle, size); - } - finally - { - ReleaseEntrySpinLock(handle); + SliceMemory(handle, (segmentIndex + 1) * levelSizeArray[level]); + SetEntryState(handle, EntryState.Allocated); } + + return new hlsfHandle(handle, size); } private void SliceMemory(Entry* e, int offset) @@ -112,123 +102,94 @@ namespace StgSharp.HighPerformance.Memory ulong end = e->Position + e->Size - (ulong)m_Buffer; int remainSize = (int)(e->Size - offset); - AcquireEntryBothLocks(e); - if (next != null) { - AcquireEntryPrevLock(next); + if ((originOffset & 63) != 0) { + throw new InvalidOperationException("Entry position offset is not aligned to 64 bytes."); } - try + int minLevel = Math.Max((BitOperations.TrailingZeroCount(originOffset) - 6) / 2, 0); + int i = minLevel >= levelSizeArray.Length ? levelSizeArray.Length - 1 : minLevel; + + for (; i < levelSizeArray.Length; i++) { - if ((originOffset & 63) != 0) { - throw new InvalidOperationException("Entry position offset is not aligned to 64 bytes."); - } - - int minLevel = Math.Max((BitOperations.TrailingZeroCount(originOffset) - 6) / 2, 0); - int i = minLevel >= levelSizeArray.Length ? levelSizeArray.Length - 1 : minLevel; - - for (; i < levelSizeArray.Length; i++) + int levelSize = levelSizeArray[i]; + ulong board = (finalOffset + (ulong)levelSize - 1) & ~((ulong)levelSize - 1); + int blockCount = (int)(ulong.Min(board, end) / (ulong)levelSize); + if (blockCount == 0) { - int levelSize = levelSizeArray[i]; - ulong board = (finalOffset + (ulong)levelSize - 1) & ~((ulong)levelSize - 1); - int blockCount = (int)(ulong.Min(board, end) / (ulong)levelSize); - if (blockCount == 0) - { - break; - } - int s = blockCount * levelSize; - - // create and init new entry - Entry* newEntry = (Entry*)_entries.Allocate(); - newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset); - newEntry->Size = (uint)s; - newEntry->LockBuffer = 0; - BucketNode* bucket = (BucketNode*)newEntry->Position; - bucket->EntryRef = newEntry; - bucket->LockBuffer = 0; - newEntry->State = EntryState.ThreadOccupied; - - // link new entry - newEntry->PreviousNear = (ulong)current; - newEntry->NextNear = n; - current->NextNear = (ulong)newEntry; - if (next != null) { - next->PreviousNear = (ulong)newEntry; - } - - newEntry->Level = i; - PushLevel(i, blockCount - 1, bucket); - - // set new entry - Entry* old = current; - current = newEntry; - - // update lock and data - finalOffset += (ulong)s; - remainSize -= s; - ReleaseEntryNextLock(old); - AcquireEntryNextLock(current); + break; } + int s = blockCount * levelSize; - if (remainSize <= 0) { - return; - } + // create and init new entry + Entry* newEntry = (Entry*)_entries.Allocate(); + newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset); + newEntry->Size = (uint)s; + BucketNode* bucket = (BucketNode*)newEntry->Position; + bucket->EntryRef = newEntry; + newEntry->State = EntryState.ThreadOccupied; - for (; i >= 0; i--) - { - int levelSize = levelSizeArray[i]; - ulong board = (finalOffset + (ulong)levelSize - 1) & ~((ulong)levelSize - 1); - int blockCount = (int)(ulong.Min(board, end) / (ulong)levelSize); - if (blockCount == 0) - { - continue; - } - int s = blockCount * levelSize; - - // create and init new entry - Entry* newEntry = (Entry*)_entries.Allocate(); - newEntry->LockBuffer = 0; - newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset); - newEntry->Size = (uint)s; - BucketNode* bucket = (BucketNode*)newEntry->Position; - bucket->EntryRef = newEntry; - bucket->LockBuffer = 0; - newEntry->State = EntryState.ThreadOccupied; - - // link new entry - newEntry->PreviousNear = (ulong)current; - newEntry->NextNear = n; - current->NextNear = (ulong)newEntry; - if (next != null) { - next->PreviousNear = (ulong)newEntry; - } - - newEntry->Level = i; - PushLevel(i, blockCount - 1, bucket); - - // set new entry - Entry* old = current; - current = newEntry; - - // update lock and data - finalOffset += (ulong)s; - remainSize -= s; - ReleaseEntryNextLock(old); - AcquireEntryNextLock(current); - if (remainSize <= 0) - { - break; - } - } - } - catch (Exception) - { - throw; - } - finally - { - ReleaseEntryBothLocks(e); + // link new entry + newEntry->PreviousNear = (ulong)current; + newEntry->NextNear = n; + current->NextNear = (ulong)newEntry; if (next != null) { - ReleaseEntryPrevLock(next); + next->PreviousNear = (ulong)newEntry; + } + + newEntry->Level = i; + PushLevel(i, blockCount - 1, bucket); + + // set new entry + current = newEntry; + + // update data + finalOffset += (ulong)s; + remainSize -= s; + } + + if (remainSize <= 0) { + return; + } + + for (i -= 1; i >= 0; i--) + { + int levelSize = levelSizeArray[i]; + ulong board = (finalOffset + (ulong)levelSize - 1) & ~((ulong)levelSize - 1); + int blockCount = (int)(ulong.Min(board, end) / (ulong)levelSize); + if (blockCount == 0) + { + continue; + } + int s = blockCount * levelSize; + + // create and init new entry + Entry* newEntry = (Entry*)_entries.Allocate(); + newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset); + newEntry->Size = (uint)s; + BucketNode* bucket = (BucketNode*)newEntry->Position; + bucket->EntryRef = newEntry; + newEntry->State = EntryState.ThreadOccupied; + + // link new entry + newEntry->PreviousNear = (ulong)current; + newEntry->NextNear = n; + current->NextNear = (ulong)newEntry; + if (next != null) { + next->PreviousNear = (ulong)newEntry; + } + + newEntry->Level = i; + PushLevel(i, blockCount - 1, bucket); + + // set new entry + current = newEntry; + + // update data + finalOffset += (ulong)s; + remainSize -= s; + if (remainSize <= 0) + { + break; } } } @@ -244,22 +205,13 @@ namespace StgSharp.HighPerformance.Memory /// private bool TryAllocateNew16MBBlock(out Entry* handle) { - nuint pos = Volatile.Read(ref _spareMemory->Position); + nuint pos = _spareMemory->Position; nuint blockSize = (nuint)levelSizeArray[^1] * 2; // Use last level size (16MB) nuint newPos = pos + blockSize; - ulong p; - Entry* previous; - do - { - p = Volatile.Read(ref _spareMemory->PreviousNear); - previous = (Entry*)p; - if (previous != null) { - AcquireEntryNextLock(previous); - } - AcquireEntryPrevLock(_spareMemory); - break; - } while (true); + ulong p = _spareMemory->PreviousNear; + Entry* previous = (Entry*)p; + if (_spareMemory->Size <= 0) { handle = null; @@ -274,42 +226,26 @@ namespace StgSharp.HighPerformance.Memory } // Try to allocate new Entry from slab allocator - try - { - handle = (Entry*)_entries.Allocate(); - handle->Position = pos; - handle->Size = (uint)blockSize; - SetEntryState(handle, EntryState.ThreadOccupied); - handle->Level = GetLevelFromSize((uint)blockSize); + handle = (Entry*)_entries.Allocate(); + handle->Position = pos; + handle->Size = (uint)blockSize; + SetEntryState(handle, EntryState.ThreadOccupied); + handle->Level = GetLevelFromSize((uint)blockSize); - handle->PrevLock = 0; - handle->NextLock = 0; + // Update spare memory position + _spareMemory->Position = newPos; + BucketNode* b = (BucketNode*)handle->Position; + b->EntryRef = handle; + PushLevel(handle->Level, DetermineSegmentIndex((uint)blockSize, handle->Level), b); - // Update spare memory position - _spareMemory->Position = newPos; - BucketNode* b = (BucketNode*)handle->Position; - b->EntryRef = handle; - PushLevel(handle->Level, DetermineSegmentIndex((uint)blockSize, handle->Level), b); - - // update position linked list - handle->NextNear = (ulong)_spareMemory; - handle->PreviousNear = p; - _spareMemory->PreviousNear = (ulong)handle; - if (previous != null) { - previous->NextNear = (ulong)handle; - } - return true; - } - catch - { - handle = null; - return false; - } - finally - { - ReleaseEntryNextLock(previous); - ReleaseEntryPrevLock(_spareMemory); + // update position linked list + handle->NextNear = (ulong)_spareMemory; + handle->PreviousNear = p; + _spareMemory->PreviousNear = (ulong)handle; + if (previous != null) { + previous->NextNear = (ulong)handle; } + return true; } /// diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.BucketNode.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.BucketNode.cs index b10351c3..b5a13595 100644 --- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.BucketNode.cs +++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.BucketNode.cs @@ -39,12 +39,6 @@ namespace StgSharp.HighPerformance.Memory public unsafe partial class HybridLayerSegregatedFitAllocator { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AcquireBucketNodeSpinLock(BucketNode* node) - { - AcquireBucketNodeBothLocks(node); - } - /// /// Initialize bucket system (called in constructor) /// @@ -53,6 +47,16 @@ namespace StgSharp.HighPerformance.Memory _bucketHeads = new ulong[levelSizeArray.Length * 3]; } + #region BucketNode Operations + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsNullOrEmpty(BucketNode* node) + { + return node == null || node->Magic != BucketNode.MAGIC_NUMBER; + } + + #endregion + private void PushLevel(int levelIndex, int sizeIndex, BucketNode* node) { if (node == null || node->EntryRef == null) { @@ -60,270 +64,112 @@ namespace StgSharp.HighPerformance.Memory } int index = (3 * levelIndex) + sizeIndex; - if (Interlocked.CompareExchange(ref _bucketHeads[index], (nuint)node, EmptyHandle) == EmptyHandle) + if (_bucketHeads[index] == EmptyHandle) { + _bucketHeads[index] = (nuint)node; node->IsInBucket = true; node->Magic = BucketNode.MAGIC_NUMBER; + node->NextLevel = EmptyHandle; + node->PreviousLevel = EmptyHandle; return; - } else - { - while (true) - { - AcquireBucketNodeNextLock(node); - ulong head = Volatile.Read(ref _bucketHeads[index]); - node->NextLevel = head; - BucketNode* headNode = (BucketNode*)head; + } - try - { - if (Interlocked.CompareExchange(ref _bucketHeads[index], (nuint)node, head) == head) - { - if (headNode != null && !TryAcquireBucketNodePrevLock(headNode)) - { - // reset state and retry - _ = Interlocked.CompareExchange(ref _bucketHeads[index], head, (nuint)node); - Thread.Sleep(0); - continue; - } - try - { - node->IsInBucket = true; - node->Magic = BucketNode.MAGIC_NUMBER; - if (headNode != null) { - headNode->PreviousLevel = (ulong)node; - } - return; - } - finally - { - if (headNode != null) { - ReleaseBucketNodePrevLock(headNode); - } - } - } else - { - Thread.Sleep(0); - continue; - } - } - finally - { - ReleaseBucketNodeNextLock(node); - } - } - } - } - /// - /// ¸ù¾ÝËøÀàÐÍÊͷÅBucketNodeËø - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReleaseBucketNodeLockByType(BucketNode* node, byte lockType) - { - switch (lockType) - { - case 0: - ReleaseBucketNodePrevLock(node); - return; // PrevLock - case 1: - ReleaseBucketNodeBothLocks(node); - return; // BothLocks - case 2: - ReleaseBucketNodeNextLock(node); - return; // NextLock - default: - return; - } - } + // Add to head of linked list + ulong head = _bucketHeads[index]; + BucketNode* headNode = (BucketNode*)head; - /// - /// °´µØַ˳ÐòÊͷÅBucketNodeËø - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReleaseOrderedBucketLocks(BucketNode* previous, BucketNode* node, BucketNode* next) - { - if (previous != null) { - ReleaseBucketNodeNextLock(previous); - } - if (node != null) { - ReleaseBucketNodeBothLocks(node); - } - if (next != null) { - ReleaseBucketNodePrevLock(next); + node->NextLevel = head; + node->PreviousLevel = EmptyHandle; + node->IsInBucket = true; + node->Magic = BucketNode.MAGIC_NUMBER; + + if (headNode != null) { + headNode->PreviousLevel = (ulong)node; } + + _bucketHeads[index] = (nuint)node; } private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node) { int index = (3 * levelIndex) + sizeIndex; - while (true) + + if ((BucketNode*)_bucketHeads[index] == node) { - if ((BucketNode*)Volatile.Read(ref _bucketHeads[index]) == node) - { - if (TryPopLevel(levelIndex, sizeIndex, out _)) { - return; - } - Thread.Sleep(0); - continue; - } - - ulong p = Volatile.Read(ref node->PreviousLevel); - ulong n = Volatile.Read(ref node->NextLevel); - BucketNode* previous = (BucketNode*)p; - BucketNode* next = (BucketNode*)n; - - if (!TryAcquireOrderedBucketLocks(previous, node, next)) - { - Thread.Sleep(0); - continue; - } - - try - { - if (Volatile.Read(ref node->PreviousLevel) != p || Volatile.Read(ref node->NextLevel) != n) - { - Thread.Sleep(0); - continue; - } - - if (previous != null) { - previous->NextLevel = n; - } - if (next != null) { - next->PreviousLevel = p; - } - - node->NextLevel = EmptyHandle; - node->PreviousLevel = EmptyHandle; - node->IsInBucket = false; - return; - } - finally - { - ReleaseOrderedBucketLocks(previous, node, next); - } + _bucketHeads[index] = EmptyHandle; + return; } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryAcquireBucketNodeSpinLock(BucketNode* node) - { - return TryAcquireBucketNodeBothLocks(node); - } + ulong p = node->PreviousLevel; + ulong n = node->NextLevel; + BucketNode* previous = (BucketNode*)p; + BucketNode* next = (BucketNode*)n; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryAcquireOrderedBucketLocks(BucketNode* previous, BucketNode* node, BucketNode* next) - { - if (previous != null && node != null && next != null) - { - // previous -> node -> next - return TryAcquireBucketNodeNextLock(previous) && - TryAcquireBucketNodeBothLocks(node) && - TryAcquireBucketNodePrevLock(next); - } else if (previous != null && node != null) { - return TryAcquireBucketNodeNextLock(previous) && TryAcquireBucketNodeBothLocks(node); + if (previous != null) { + previous->NextLevel = n; } - return true; + if (next != null) { + next->PreviousLevel = p; + } + + node->NextLevel = EmptyHandle; + node->PreviousLevel = EmptyHandle; + node->IsInBucket = false; } private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node) { int index = (3 * levelIndex) + sizeIndex; - while (true) + ulong h = _bucketHeads[index]; + BucketNode* head = (BucketNode*)h; + + if (head == null) { - ulong h = Volatile.Read(ref _bucketHeads[index]); - BucketNode* head = (BucketNode*)h; - - if (head == null) - { - node = null; - return false; - } - - if (!TryAcquireBucketNodeNextLock(head)) - { - Thread.Sleep(0); - continue; - } - - ulong n = Volatile.Read(ref head->NextLevel); - BucketNode* next = (BucketNode*)n; - - if (next == null) - { - if (Interlocked.CompareExchange(ref _bucketHeads[index], EmptyHandle, h) == h) - { - node = head; - return true; - } - node = null; - return false; - } - try - { - if (!TryAcquireBucketNodePrevLock(next)) - { - Thread.Sleep(0); - continue; - } - - try - { - if (Interlocked.CompareExchange(ref _bucketHeads[index], head->NextLevel, h) == h) - { - node = head; - node->IsInBucket = false; - node->NextLevel = EmptyHandle; - if (next != null) { - next->PreviousLevel = EmptyHandle; - } - return true; - } else - { - Thread.Sleep(0); - continue; - } - } - finally - { - if (next != null) { - ReleaseBucketNodePrevLock(next); - } - } - } - finally - { - ReleaseBucketNodeNextLock(head); - } + node = null; + return false; } + + ulong n = head->NextLevel; + BucketNode* next = (BucketNode*)n; + + if (next == null) + { + _bucketHeads[index] = EmptyHandle; + node = head; + node->IsInBucket = false; + node->NextLevel = EmptyHandle; + node->PreviousLevel = EmptyHandle; + return true; + } + + _bucketHeads[index] = head->NextLevel; + node = head; + node->IsInBucket = false; + node->NextLevel = EmptyHandle; + + if (next != null) { + next->PreviousLevel = EmptyHandle; + } + + return true; } private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node) { int index = (3 * levelIndex) + sizeIndex; - if ((BucketNode*)Volatile.Read(ref _bucketHeads[index]) == node) + if ((BucketNode*)_bucketHeads[index] == node) { - if (TryPopLevel(levelIndex, sizeIndex, out _)) { - return true; - } - return false; - } - - ulong p = Volatile.Read(ref node->PreviousLevel); - ulong n = Volatile.Read(ref node->NextLevel); - BucketNode* previous = (BucketNode*)p; - BucketNode* next = (BucketNode*)n; - - if (!TryAcquireOrderedBucketLocks(previous, node, next)) { - return false; + _bucketHeads[index] = EmptyHandle; + return true; } try { - if (Volatile.Read(ref node->PreviousLevel) != p || Volatile.Read(ref node->NextLevel) != n) { - return false; - } - + ulong p = node->PreviousLevel; + ulong n = node->NextLevel; + BucketNode* previous = (BucketNode*)p; + BucketNode* next = (BucketNode*)n; if (previous != null) { previous->NextLevel = n; @@ -337,9 +183,9 @@ namespace StgSharp.HighPerformance.Memory node->IsInBucket = false; return true; } - finally + catch { - ReleaseOrderedBucketLocks(previous, node, next); + return false; } } @@ -347,7 +193,7 @@ namespace StgSharp.HighPerformance.Memory /// BucketNode structure - specialized node for bucket linked lists Stored in main /// allocation area (user-overwritable region) /// - [StructLayout(LayoutKind.Explicit, Size = 40)] + [StructLayout(LayoutKind.Explicit, Size = 32)] internal struct BucketNode { @@ -355,181 +201,12 @@ namespace StgSharp.HighPerformance.Memory [FieldOffset(16)] internal Entry* EntryRef; // Pointer to corresponding Entry [FieldOffset(28)] internal bool IsInBucket; // Whether in bucket - [FieldOffset(36)] internal int NextLock; - [FieldOffset(32)] internal int PrevLock; [FieldOffset(24)] internal uint Magic; // Magic number verification - [FieldOffset(32)] internal ulong LockBuffer; [FieldOffset(0)] internal ulong NextLevel; // Bucket stack linked list: next pointer [FieldOffset(8)] internal ulong PreviousLevel; // Bucket stack linked list: previous pointer } - #region BucketNode Operations - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsNullOrEmpty(BucketNode* node) - { - return node == null || node->Magic != BucketNode.MAGIC_NUMBER; - } - - // BucketNode ǰÇýËø²Ù×÷£¨¿ØÖÆPreviousLevel×ֶΣ© - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryAcquireBucketNodePrevLock(BucketNode* node) - { - if (node == null) { - return true; - } - - int thread = Environment.CurrentManagedThreadId; - return - Interlocked.CompareExchange(ref node->PrevLock, thread, 0) == 0 || - Volatile.Read(ref node->PrevLock) == thread; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AcquireBucketNodePrevLock(BucketNode* node) - { - if (node == null) { - return; - } - - int thread = Environment.CurrentManagedThreadId; - if (Volatile.Read(ref node->PrevLock) != thread) - { - while (Interlocked.CompareExchange(ref node->PrevLock, thread, 0) != 0) { - Thread.Sleep(0); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReleaseBucketNodePrevLock(BucketNode* node) - { - if (node == null) { - return; - } - - int thread = Environment.CurrentManagedThreadId; - if (Volatile.Read(ref node->PrevLock) == thread) { - Volatile.Write(ref node->PrevLock, 0); - } - } - - // BucketNode ºó¼ÌËø²Ù×÷£¨¿ØÖÆNextLevel×ֶΣ© - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryAcquireBucketNodeNextLock(BucketNode* node) - { - if (node == null) { - return true; - } - - int thread = Environment.CurrentManagedThreadId; - return - Interlocked.CompareExchange(ref node->NextLock, thread, 0) == 0 || - Volatile.Read(ref node->NextLock) == thread; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AcquireBucketNodeNextLock(BucketNode* node) - { - if (node == null) { - return; - } - - int thread = Environment.CurrentManagedThreadId; - if (Volatile.Read(ref node->NextLock) != thread) - { - while (Interlocked.CompareExchange(ref node->NextLock, thread, 0) != 0) { - Thread.Sleep(0); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReleaseBucketNodeNextLock(BucketNode* node) - { - if (node == null) { - return; - } - - int thread = Environment.CurrentManagedThreadId; - if (Volatile.Read(ref node->NextLock) == thread) { - Volatile.Write(ref node->NextLock, 0); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryAcquireBucketNodeBothLocks(BucketNode* node) - { - if (node == null) { - return true; - } - - int thread = Environment.CurrentManagedThreadId; - int retryCount = 0; - - while (retryCount < 1000) - { - if (Interlocked.CompareExchange(ref node->PrevLock, thread, 0) == 0 || - Volatile.Read(ref node->PrevLock) == thread) - { - if (Interlocked.CompareExchange(ref node->NextLock, thread, 0) == 0 || - Volatile.Read(ref node->NextLock) == thread) - { - return true; - } else - { - Volatile.Write(ref node->PrevLock, 0); - } - } - - retryCount++; - Thread.Sleep(0); // Èóö CPU - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AcquireBucketNodeBothLocks(BucketNode* node) - { - if (node == null) { - return; - } - - int thread = Environment.CurrentManagedThreadId; - - while (true) - { - if (Interlocked.CompareExchange(ref node->PrevLock, thread, 0) == 0 || - Volatile.Read(ref node->PrevLock) == thread) - { - if (Interlocked.CompareExchange(ref node->NextLock, thread, 0) == 0 || - Volatile.Read(ref node->NextLock) == thread) - { - return; - } else - { - Volatile.Write(ref node->PrevLock, 0); - } - } - - Thread.Sleep(0); // Èóö CPU - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReleaseBucketNodeBothLocks(BucketNode* node) - { - if (node == null) { - return; - } - - ReleaseBucketNodePrevLock(node); - ReleaseBucketNodeNextLock(node); - } - - #endregion } } diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs index 5b0caf00..77a34c82 100644 --- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs +++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Free.cs @@ -47,33 +47,32 @@ namespace StgSharp.HighPerformance.Memory if (e == null) { return; } - AcquireEntrySpinLock(e); - try - { - if (GetEntryState(e) != EntryState.Allocated) { - return; - } - SetEntryState(e, EntryState.ThreadOccupied); - // Step 4: Try merge with adjacent free blocks and remove them from buckets - MergeAndRemoveFromBuckets(e); - - // Step 5: Calculate final level and segment for the merged block - uint finalSize = e->Size; - int finalLevel = GetLevelFromSize(finalSize); - int finalSegment = DetermineSegmentIndex(finalSize, finalLevel); - - // Step 6: Set the merged entry state to Empty (ready to be reused) - SetEntryState(e, EntryState.Empty); - - // Step 7: Push the merged entry back to appropriate bucket - PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position); - } - finally - { - // Step 8: Always release the spin lock - ReleaseEntrySpinLock(e); + if (GetEntryState(e) != EntryState.Allocated) { + return; } + + + // Set to ThreadOccupied to prevent other operations + SetEntryState(e, EntryState.ThreadOccupied); + + + // Try merge with adjacent free blocks and remove them from buckets + MergeAndRemoveFromBuckets(e); + + + // Calculate final level and segment for the merged block + uint finalSize = e->Size; + int finalLevel = GetLevelFromSize(finalSize); + int finalSegment = DetermineSegmentIndex(finalSize, finalLevel); + + + // Set the merged entry state to Empty (ready to be reused) + SetEntryState(e, EntryState.Empty); + + + // Push the merged entry back to appropriate bucket + PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position); } } diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.SegmentEntry.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.SegmentEntry.cs index 5271ad9a..a31ab50c 100644 --- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.SegmentEntry.cs +++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.SegmentEntry.cs @@ -41,12 +41,6 @@ namespace StgSharp.HighPerformance.Memory public unsafe partial class HybridLayerSegregatedFitAllocator { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AcquireEntrySpinLock(Entry* entry) - { - AcquireEntryBothLocks(entry); - } - private bool AssertBackwardBoundary(ulong current, Entry* target, int level) { ulong align = (ulong)levelSizeArray[level + 1]; @@ -83,8 +77,7 @@ namespace StgSharp.HighPerformance.Memory /// /// 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 - /// strategy, any fail in acquiring spin lock will end a merging process. + /// Current Entry will serve as the final merged block. /// private void MergeAndRemoveFromBuckets(Entry* entry) { @@ -92,185 +85,127 @@ namespace StgSharp.HighPerformance.Memory return; } - Entry* result = entry; uint size = entry->Size; int originLevel = GetLevelFromSize(size); int segment = DetermineSegmentIndex(size, originLevel); - if (!TryAcquireEntrySpinLock(entry)) { - return; - } - if (!TryRemoveFromBucket(originLevel, segment, (BucketNode*)entry->Position)) { } - try + RemoveFromBucket(originLevel, segment, (BucketNode*)entry->Position); + + SetEntryState(entry, EntryState.Merging); + + while (originLevel >= entry->Level) { - _ = TrySetEntryState(entry, EntryState.Empty, EntryState.Merging); - - // current Level merge - while (originLevel <= entry->Level) + originLevel = entry->Level; + while (true) { - ulong offset = entry->Position - (ulong)m_Buffer; - - // backward merging - while (true) - { - ulong p = entry->PreviousNear; - Entry* prev = (Entry*)p; - if (prev == null || // null ref - prev == _spareMemory || // point at end of list - GetEntryState(prev) != EntryState.Empty || // not empty - !IsAdjacentTo(entry, prev) || // not nearby - entry->Level != prev->Level || // not same level - !AssertBackwardBoundary(offset, prev, entry->Level) || // not aligned if merge - !TryAcquireEntrySpinLock(prev)) // occupied by threads - { - break; - } - ulong pp = prev->NextNear; - Entry* prevPrev = (Entry*)pp; - if (!TryAcquireEntryNextLock(prevPrev)) - { - break; - } - try - { - if (!TryRemoveFromBucket(originLevel, DetermineSegmentIndex(prev->Size, originLevel), (BucketNode*)prev->Position)) - { - break; - } - entry->Position = prev->Position; // Update position to the previous entry - entry->Size += prev->Size; // Merge sizes - prevPrev->NextNear = (ulong)entry; - RecycleEntry(prev); - } - finally - { - ReleaseEntryNextLock(prevPrev); - ReleaseEntrySpinLock(prev); - } - } - - // forward merging - while (true) - { - ulong n = entry->NextNear; - Entry* next = (Entry*)n; - if (next == null || // null ref - next == _spareMemory || // point at end of list - GetEntryState(next) != EntryState.Empty || // not empty - !IsAdjacentTo(entry, next) || // not nearby - entry->Level != next->Level || // not same level - !AssertBackwardBoundary(offset, next, entry->Level) || // not aligned if merge - !TryAcquireEntrySpinLock(next)) // occupied by threads - { - break; - } - ulong nn = next->NextNear; - Entry* nextNext = (Entry*)nn; - if (!TryAcquireEntryPrevLock(nextNext)) - { - break; - } - try - { - if (!TryRemoveFromBucket(originLevel, DetermineSegmentIndex(next->Size, originLevel), (BucketNode*)next->Position)) - { - break; - } - entry->Size += next->Size; - entry->NextNear = next->NextNear; - RecycleEntry(next); - } - finally - { - ReleaseEntrySpinLock(next); - } - } - - entry->Level = GetLevelFromSize(entry->Size); - if (originLevel == entry->Level) + // backward merging - simplified to single attempt + ulong p = entry->PreviousNear; + Entry* prev = (Entry*)p; + if (prev != null || + prev != _spareMemory || + GetEntryState(prev) == EntryState.Empty || + !AssertBackwardBoundary(entry->Position, prev, originLevel) || + IsAdjacentTo(entry, prev) || + entry->Level == prev->Level || + !TryRemoveFromBucket(originLevel, DetermineSegmentIndex(prev->Size, originLevel), (BucketNode*)prev->Position)) { break; } - originLevel = entry->Level; + + // Update linked list + Entry* prevPrev = (Entry*)prev->PreviousNear; + if (prevPrev != null) { + prevPrev->NextNear = (ulong)entry; + } + entry->PreviousNear = (ulong)prevPrev; + + // Merge + entry->Position = prev->Position; + entry->Size += prev->Size; + RecycleEntry(prev); } - _ = TrySetEntryState(entry, EntryState.Merging, EntryState.Empty); - return; - } - finally - { - ReleaseEntrySpinLock(entry); + while (true) + { + // forward merging - simplified to single attempt + ulong n = entry->NextNear; + Entry* next = (Entry*)n; + if (next != null || + next != _spareMemory || + GetEntryState(next) == EntryState.Empty || + !AssertForwardBoundary(entry->Position + entry->Size, next, entry->Level) || + IsAdjacentTo(entry, next) || + entry->Level == next->Level || + !TryRemoveFromBucket(originLevel, DetermineSegmentIndex(next->Size, originLevel), (BucketNode*)next->Position)) + { + break; + } + + // Update linked list + Entry* nextNext = (Entry*)next->NextNear; + if (nextNext != null) { + nextNext->PreviousNear = (ulong)entry; + } + entry->NextNear = (ulong)nextNext; + + + // Merge + entry->Size += next->Size; + RecycleEntry(next); + } + entry->Level = GetLevelFromSize(entry->Size); } + SetEntryState(entry, EntryState.Empty); } private void RecycleEntry(Entry* entry) { - if (entry is null) { + if (entry == null) { return; } - AcquireEntrySpinLock(entry); - entry->LockBuffer = 0; + entry->State = EntryState.Empty; entry->PreviousNear = EmptyHandle; entry->NextNear = EmptyHandle; _entries.Free((nuint)entry); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReleaseEntrySpinLock(Entry* entry) - { - ReleaseEntryBothLocks(entry); - } - /// /// Remove node from position linked list /// private void RemoveFromPositionChain(Entry* entry) { - AcquireEntrySpinLock(entry); - try - { - Entry* prev = (Entry*)entry->PreviousNear; - Entry* next = (Entry*)entry->NextNear; - - if (prev != null) { - prev->NextNear = entry->NextNear; - } - if (next != null) { - next->PreviousNear = entry->PreviousNear; - } - - // Clear node pointers - entry->NextNear = EmptyHandle; - entry->PreviousNear = EmptyHandle; - _entries.Free((nuint)entry); + if (entry == null) { + return; } - finally - { - ReleaseEntrySpinLock(entry); - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryAcquireEntrySpinLock(Entry* entry) - { - return TryAcquireEntryBothLocks(entry); + Entry* prev = (Entry*)entry->PreviousNear; + Entry* next = (Entry*)entry->NextNear; + + if (prev != null) { + prev->NextNear = entry->NextNear; + } + if (next != null) { + next->PreviousNear = entry->PreviousNear; + } + + // Clear node pointers + entry->NextNear = EmptyHandle; + entry->PreviousNear = EmptyHandle; + _entries.Free((nuint)entry); } /// /// Entry structure - streamlined version (only position and state information) Stored in /// external SLAB /// - [StructLayout(LayoutKind.Explicit, Size = 44)] + [StructLayout(LayoutKind.Explicit, Size = 32)] internal struct Entry { [FieldOffset(28)] internal int Level; // Level of the block in the allocator - [FieldOffset(32)] internal int NextLock; - [FieldOffset(36)] internal int PrevLock; - [FieldOffset(40)] internal int State; // Node state (using int for atomic operations) + [FieldOffset(20)] internal int State; // Node state (using int for atomic operations) [FieldOffset(24)] internal uint Size; // Block size information - [FieldOffset(32)] internal ulong LockBuffer; [FieldOffset(8)] internal ulong NextNear; // Position linked list: next pointer [FieldOffset(16)] internal ulong PreviousNear; // Position linked list: previous pointer [FieldOffset(0)] public nuint Position; // Memory block position @@ -306,19 +241,26 @@ namespace StgSharp.HighPerformance.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TrySetEntryState(Entry* entry, int expectedState, int newState) { - return Interlocked.CompareExchange(ref entry->State, newState, expectedState) == expectedState; + if (entry != null && entry->State == expectedState) + { + entry->State = newState; + return true; + } + return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetEntryState(Entry* entry) { - return Volatile.Read(ref entry->State); + return entry != null ? entry->State : 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void SetEntryState(Entry* entry, int newState) { - Volatile.Write(ref entry->State, newState); + if (entry != null) { + entry->State = newState; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -345,154 +287,6 @@ namespace StgSharp.HighPerformance.Memory return GetEntryState(entry) == EntryState.Merging; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryAcquireEntryPrevLock(Entry* entry) - { - if (entry == null) { - return true; - } - - int thread = Environment.CurrentManagedThreadId; - return - Interlocked.CompareExchange(ref entry->PrevLock, thread, 0) == 0 || - Volatile.Read(ref entry->PrevLock) == thread; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AcquireEntryPrevLock(Entry* entry) - { - if (entry == null) { - return; - } - - int thread = Environment.CurrentManagedThreadId; - if (Volatile.Read(ref entry->PrevLock) != thread) - { - while (Interlocked.CompareExchange(ref entry->PrevLock, thread, 0) != 0) { - Thread.Sleep(0); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReleaseEntryPrevLock(Entry* entry) - { - if (entry == null) { - return; - } - - int thread = Environment.CurrentManagedThreadId; - if (Volatile.Read(ref entry->PrevLock) == thread) { - Volatile.Write(ref entry->PrevLock, 0); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryAcquireEntryNextLock(Entry* entry) - { - if (entry == null) { - return true; - } - - int thread = Environment.CurrentManagedThreadId; - return - Interlocked.CompareExchange(ref entry->NextLock, thread, 0) == 0 || - Volatile.Read(ref entry->NextLock) == thread; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AcquireEntryNextLock(Entry* entry) - { - if (entry == null) { - return; - } - - int thread = Environment.CurrentManagedThreadId; - if (Volatile.Read(ref entry->NextLock) != thread) - { - while (Interlocked.CompareExchange(ref entry->NextLock, thread, 0) != 0) { - Thread.Sleep(0); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReleaseEntryNextLock(Entry* entry) - { - if (entry == null) { - return; - } - - int thread = Environment.CurrentManagedThreadId; - if (Volatile.Read(ref entry->NextLock) == thread) { - Volatile.Write(ref entry->NextLock, 0); - } - } - - // Entry 双锁组合操作 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryAcquireEntryBothLocks(Entry* entry) - { - if (entry == null) { - return true; - } - - int thread = Environment.CurrentManagedThreadId; - - // 尝试同时获取前后锁 - if (Interlocked.CompareExchange(ref entry->PrevLock, thread, 0) == 0 || - Volatile.Read(ref entry->PrevLock) == thread) - { - if (Interlocked.CompareExchange(ref entry->NextLock, thread, 0) == 0 || - Volatile.Read(ref entry->NextLock) == thread) - { - return true; - } else - { - // 回滚前锁 - Volatile.Write(ref entry->PrevLock, 0); - } - } - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReleaseEntryBothLocks(Entry* entry) - { - if (entry == null) { - return; - } - - ReleaseEntryPrevLock(entry); - ReleaseEntryNextLock(entry); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AcquireEntryBothLocks(Entry* entry) - { - if (entry == null) { - return; - } - - int thread = Environment.CurrentManagedThreadId; - - // 循环直到成功获取两个锁 - while (true) - { - if ((Interlocked.CompareExchange(ref entry->PrevLock, thread, 0) == 0 || - Volatile.Read(ref entry->PrevLock) == thread) && - (Interlocked.CompareExchange(ref entry->NextLock, thread, 0) == 0 || - Volatile.Read(ref entry->NextLock) == thread)) { - return; - } - - // 回滚前锁 - Volatile.Write(ref entry->PrevLock, 0); - Thread.Sleep(0); - } - } - #endregion } } diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs index 66fdec9d..1e84001c 100644 --- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs +++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.cs @@ -66,8 +66,6 @@ namespace StgSharp.HighPerformance.Memory _spareMemory->PreviousNear = EmptyHandle; _spareMemory->NextNear = EmptyHandle; _spareMemory->Position = (nuint)m_Buffer; - _spareMemory->PrevLock = 0; - _spareMemory->NextLock = 0; _spareMemory->Size = (uint)byteSize; } @@ -89,8 +87,6 @@ namespace StgSharp.HighPerformance.Memory _spareMemory->PreviousNear = EmptyHandle; _spareMemory->NextNear = EmptyHandle; _spareMemory->Position = (nuint)m_Buffer; - _spareMemory->PrevLock = 0; - _spareMemory->NextLock = 0; _spareMemory->Size = (uint)byteSize; } diff --git a/StgSharpDebug/Program.cs b/StgSharpDebug/Program.cs index 4ba7e870..ff4aa548 100644 --- a/StgSharpDebug/Program.cs +++ b/StgSharpDebug/Program.cs @@ -63,62 +63,163 @@ namespace StgSharp // HLSF test arena Console.WriteLine( - "=== HLSF (Hybrid Layer Segregated Fit Allocator) Multi-threaded Test Arena ===\n"); - HybridLayerSegregatedFitAllocator hlsfAllocator = new HybridLayerSegregatedFitAllocator(1024 * 1024 * 4); + "=== HLSF (Hybrid Layer Segregated Fit Allocator) Single-threaded Stress Test ===\n"); - int threadCount = 4; - int blocksPerThread = 1024; - int blockSize = 1024; - Thread[] threads = new Thread[threadCount]; - for (int t = 0; t < threadCount; t++) + // 使用适中的内存池大小 + HybridLayerSegregatedFitAllocator hlsfAllocator = new HybridLayerSegregatedFitAllocator(16 * 1024 * 1024); + + Console.WriteLine("开始单线程压力测试..."); + + + // 单线程压力测试:适度的分配和释放操作 + int totalCycles = 5; + int allocationsPerCycle = 1024 * 64; // 65536 次分配 + + for (int cycle = 0; cycle < totalCycles; cycle++) { - threads[t] = new Thread(() => { - int threadIndex = Environment.CurrentManagedThreadId; - Stack handles = new(); + Console.WriteLine($"\n=== 第 {cycle + 1} 轮压力测试 ==="); - // Allocate blocks - for (int i = 0; i < blocksPerThread; i++) + // 测试不同大小的分配 + List handles = new List(); + Random random = new Random(cycle); // 使用固定种子以确保可重现性 + + + // 分配阶段 + Console.WriteLine($"分配阶段:分配 {allocationsPerCycle} 个内存块..."); + Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew(); + + for (int i = 0; i < allocationsPerCycle; i++) + { + // 随机大小:64字节到1KB + uint size = (uint)(64 + random.Next(1024 - 64 + 1)); + handles.Add(hlsfAllocator.Alloc(size)); + + if (i % 5000 == 0 && i > 0) { + Console.WriteLine($" 已分配 {i} 个块"); + } + } + + stopwatch.Stop(); + Console.WriteLine($"分配完成,总计分配 {handles.Count} 个块"); + Console.WriteLine($"分配耗时: {stopwatch.ElapsedMilliseconds} ms"); + if (stopwatch.ElapsedMilliseconds > 0) { + Console.WriteLine($"分配速度: {allocationsPerCycle * 1000 / stopwatch.ElapsedMilliseconds} allocs/sec"); + } + + + // 随机释放阶段 + Console.WriteLine("随机释放阶段..."); + stopwatch.Restart(); + + + // 随机打乱释放顺序 + for (int i = handles.Count - 1; i > 0; i--) + { + int j = random.Next(i + 1); + (handles[i], handles[j]) = (handles[j], handles[i]); + } + + for (int i = 0; i < handles.Count; i++) + { + hlsfAllocator.Free(handles[i]); + + if (i % 5000 == 0 && i > 0) { + Console.WriteLine($" 已释放 {i} 个块"); + } + } + + stopwatch.Stop(); + Console.WriteLine($"释放完成,总计释放 {handles.Count} 个块"); + Console.WriteLine($"释放耗时: {stopwatch.ElapsedMilliseconds} ms"); + if (stopwatch.ElapsedMilliseconds > 0) { + Console.WriteLine($"释放速度: {handles.Count * 1000 / stopwatch.ElapsedMilliseconds} frees/sec"); + } + + handles.Clear(); + + + // 碎片化测试 + Console.WriteLine("碎片化测试:交替分配释放..."); + List fragmentationHandles = new List(); + + for (int i = 0; i < 500; i++) // 减少碎片化测试次数 + { + // 分配5个块 + for (int j = 0; j < 5; j++) { - Console.WriteLine($"Thread {threadIndex}: Allocating block {i}."); - handles.Push(hlsfAllocator.Alloc((uint)blockSize)); + uint size = (uint)(64 << (i % 4)); // 64, 128, 256, 512 + fragmentationHandles.Add(hlsfAllocator.Alloc(size)); } - // Free blocks - while (handles.TryPop(out HybridLayerSegregatedFitAllocationHandle handle)) + + // 释放前3个块,保留后2个 + for (int j = 0; j < 3 && fragmentationHandles.Count > 0; j++) { - Console.WriteLine($"Thread {threadIndex}: Freeing a block."); - hlsfAllocator.Free(handle); + hlsfAllocator.Free(fragmentationHandles[0]); + fragmentationHandles.RemoveAt(0); } + } - Console.WriteLine($"Thread {threadIndex}: All blocks allocated and freed."); - }); + + // 释放剩余的块 + foreach (HybridLayerSegregatedFitAllocationHandle handle in fragmentationHandles) + { + hlsfAllocator.Free(handle); + } + + Console.WriteLine($"第 {cycle + 1} 轮测试完成"); } - // Start all threads - foreach (Thread thread in threads) + Console.WriteLine("\n=== 边界测试 ==="); + + + // 测试各种边界情况 + Console.WriteLine("测试最小分配 (64字节)..."); + List minHandles = new List(); + for (int i = 0; i < 1000; i++) { + minHandles.Add(hlsfAllocator.Alloc(64)); + } + foreach (HybridLayerSegregatedFitAllocationHandle handle in minHandles) { + hlsfAllocator.Free(handle); + } + Console.WriteLine("最小分配测试完成"); + + Console.WriteLine("测试最大分配 (1024字节)..."); + List maxHandles = new List(); + for (int i = 0; i < 1000; i++) { + maxHandles.Add(hlsfAllocator.Alloc(1024)); + } + foreach (HybridLayerSegregatedFitAllocationHandle handle in maxHandles) { + hlsfAllocator.Free(handle); + } + Console.WriteLine("最大分配测试完成"); + + Console.WriteLine("测试混合大小分配..."); + List mixedHandles = new List(); + uint[] testSizes = { + 64, 128, 256, 512, 1024 + }; + for (int i = 0; i < 2000; i++) { - thread.Start(); + uint size = testSizes[i % testSizes.Length]; + mixedHandles.Add(hlsfAllocator.Alloc(size)); } - - // Wait for all threads to complete - foreach (Thread thread in threads) - { - thread.Join(); + foreach (HybridLayerSegregatedFitAllocationHandle handle in mixedHandles) { + hlsfAllocator.Free(handle); } + Console.WriteLine("混合大小分配测试完成"); - Console.WriteLine("All threads completed successfully."); + Console.WriteLine("\n所有测试完成!HLSF分配器表现良好。"); + Console.WriteLine("按任意键退出..."); + Console.ReadKey(); } catch (Exception ex) { - Console.WriteLine(ex); - /* - World.WriteLog($"{ex}", LogType.Error); - if( true ) { } - Dialogue.PostError( ex ); - /**/ + Console.WriteLine($"测试过程中发生错误: {ex}"); + Console.WriteLine("按任意键退出..."); + Console.ReadKey(); World.Terminate(-1); - /**/ } finally {