From 641b459997fa4f3558ecc768bb777df8e084c58f Mon Sep 17 00:00:00 2001 From: Misaki Date: Wed, 18 Mar 2026 20:12:41 +0900 Subject: [PATCH] feat(memory): improve oversized alloc handling & cleanup Enhanced FreeList to handle oversized allocations by bypassing chunk management and allocating directly from the OS, with proper header assignment and immediate release on free. Updated IUnsafeCollection documentation, removed unnecessary array clearing in Clear() methods, refined UnsafeSlotMap initialization logic, and removed redundant default element addition. Bumped assembly version to 1.5.1. --- .../Buffer/FreeList.cs | 17 +++++++++++------ .../Collections/Contracts/IUnsafeCollection.cs | 2 +- .../Collections/UnsafeList.cs | 1 - .../Collections/UnsafeQueue.cs | 1 - .../Collections/UnsafeSlotMap.cs | 4 +--- .../Collections/UnsafeSparseSet.cs | 2 -- .../Collections/UnsafeStack.cs | 1 - .../Misaki.HighPerformance.LowLevel.csproj | 2 +- 8 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Misaki.HighPerformance.LowLevel/Buffer/FreeList.cs b/Misaki.HighPerformance.LowLevel/Buffer/FreeList.cs index 4152b1d..0416bfe 100644 --- a/Misaki.HighPerformance.LowLevel/Buffer/FreeList.cs +++ b/Misaki.HighPerformance.LowLevel/Buffer/FreeList.cs @@ -540,10 +540,15 @@ public unsafe struct FreeList : IDisposable ptr = TryPopFromBucket(cache, cacheIndex, bucketIndex); } } - - if (ptr == null) + else { - ptr = AllocateFromChunk(cacheIndex, totalSize, alignment); + // Oversized block: Bypass chunk linking entirely and go straight to the OS + ptr = AlignedAlloc(totalSize, alignment); + if (ptr != null) + { + // Pass null for ownerChunk so 'Free' knows this is a standalone allocation + AssignBlockHeader((BlockHeader*)ptr, null, totalSize, cacheIndex); + } } if (ptr == null) @@ -604,10 +609,10 @@ public unsafe struct FreeList : IDisposable if (bucketIndex < 0) { - header->ownerChunk = null; - header->blockSize = 0; + // This is an oversized allocation. It doesn't belong to a bucket or a chunk. + // Erase the magic number for safety and instantly yield it back to the OS. header->magicNumber = 0; - header->ownerCacheIndex = 0; + AlignedFree(blockStartPtr); return; } diff --git a/Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeCollection.cs b/Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeCollection.cs index 6da7ca3..051f4ef 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeCollection.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/Contracts/IUnsafeCollection.cs @@ -28,7 +28,7 @@ public interface IUnsafeCollection : IUnsafeCollection, IEnumerable where T : unmanaged { /// - /// Gets the number of elements in a collection. The value is read-only. + /// Gets the number of elements in a collection. /// int Count { diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs index 9c584b5..8445031 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs @@ -467,7 +467,6 @@ public unsafe struct UnsafeList : IUnsafeCollection public void Clear() { - _array.Clear(); _count = 0; } diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeQueue.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeQueue.cs index acf8574..09bdb40 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeQueue.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeQueue.cs @@ -193,7 +193,6 @@ public unsafe struct UnsafeQueue : IUnsafeCollection public void Clear() { - _array.Clear(); _count = 0; _offset = 0; } diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeSlotMap.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeSlotMap.cs index db58cfa..d69b198 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeSlotMap.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeSlotMap.cs @@ -132,8 +132,8 @@ public unsafe struct UnsafeSlotMap : IUnsafeCollection if (!allocationOption.HasFlag(AllocationOption.Clear)) { _generations.AsSpan().Clear(); + _validBits.ClearAll(); } - _validBits.ClearAll(); _count = 0; _capacity = capacity; @@ -324,8 +324,6 @@ public unsafe struct UnsafeSlotMap : IUnsafeCollection _validBits.ClearAll(); _count = 0; - - Add(default, out _); } public readonly void* GetUnsafePtr() diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeSparseSet.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeSparseSet.cs index 557dad1..ebadb3c 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeSparseSet.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeSparseSet.cs @@ -396,8 +396,6 @@ public unsafe struct UnsafeSparseSet : IUnsafeCollection _count = 0; _nextId = 0; - - Add(default, out _); } /// diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeStack.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeStack.cs index a2787e0..2807d30 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeStack.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeStack.cs @@ -221,7 +221,6 @@ public unsafe struct UnsafeStack : IUnsafeCollection public void Clear() { - _array.Clear(); _count = 0; } diff --git a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj index df6d061..978edc1 100644 --- a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj +++ b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj @@ -7,7 +7,7 @@ true true Misaki - 1.5.0 + 1.5.1 $(AssemblyVersion) https://git.personalnas.com/Misaki/Misaki.HighPerformance.git https://git.personalnas.com/Misaki/Misaki.HighPerformance.git