diff --git a/Misaki.HighPerformance.Test/Program.cs b/Misaki.HighPerformance.Test/Program.cs index cfdb9d2..64d0193 100644 --- a/Misaki.HighPerformance.Test/Program.cs +++ b/Misaki.HighPerformance.Test/Program.cs @@ -1,7 +1,8 @@ using Misaki.HighPerformance.Unsafe.Collections; using Misaki.HighPerformance.Unsafe.Services; +AllocationManager.Initialize(100); var unfreeArray = new UnsafeArray(10, Allocator.Persistent); -unfreeArray.Dispose(); +//unfreeArray.Dispose(); AllocationManager.Dispose(); diff --git a/Misaki.HighPerformance.Unsafe/Collections/UnsafeArray.cs b/Misaki.HighPerformance.Unsafe/Collections/UnsafeArray.cs index 368fbac..409cd08 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/UnsafeArray.cs +++ b/Misaki.HighPerformance.Unsafe/Collections/UnsafeArray.cs @@ -128,7 +128,7 @@ public unsafe struct UnsafeArray : IUnsafeCollection return; } - _buffer = (T*)AlignedRealloc(_buffer, (nuint)(newSize * sizeof(T)), AlignOf()); + _buffer = AllocationManager.Realloc(_buffer, (uint)newSize, (uint)AlignOf(), _allocator); _count = newSize; } diff --git a/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashMap.cs b/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashMap.cs index 42c2d4e..181b53b 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashMap.cs +++ b/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashMap.cs @@ -92,9 +92,9 @@ public unsafe struct UnsafeHashMap : IUnsafeCollection> GetEnumerator() => new Enumerator((HashMapHelper*)UnsafeUtilities.AddressOf(ref _hashMap)); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public UnsafeHashMap(int capacity, Allocator allocator) + public UnsafeHashMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) { - _hashMap = new HashMapHelper(capacity, sizeof(TValue), HashMapHelper.MINIMAL_CAPACITY, allocator); + _hashMap = new HashMapHelper(capacity, sizeof(TValue), HashMapHelper.MINIMAL_CAPACITY, allocator, allocationOption); } /// @@ -132,6 +132,16 @@ public unsafe struct UnsafeHashMap : IUnsafeCollection + /// Removes a particular key and its value. + /// + /// The value to remove. + /// True if the value was present. + public bool Remove(TKey key) + { + return -1 != _hashMap.TryRemove(key); + } + /// /// Returns the value associated with a key. /// diff --git a/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashSet.cs b/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashSet.cs index 37eded8..a351dba 100644 --- a/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashSet.cs +++ b/Misaki.HighPerformance.Unsafe/Collections/UnsafeHashSet.cs @@ -49,9 +49,9 @@ public unsafe struct UnsafeHashSet : IUnsafeCollection, IEnumerable public IEnumerator GetEnumerator() => new Enumerator((HashMapHelper*)UnsafeUtilities.AddressOf(ref _hashMap)); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public UnsafeHashSet(int capacity, Allocator allocator) + public UnsafeHashSet(int capacity, Allocator allocator, AllocationOption allocationOption) { - _hashMap = new HashMapHelper(capacity, 0, HashMapHelper.MINIMAL_CAPACITY, allocator); + _hashMap = new HashMapHelper(capacity, 0, HashMapHelper.MINIMAL_CAPACITY, allocator, allocationOption); } /// diff --git a/Misaki.HighPerformance.Unsafe/Helpers/HashMapHelper.cs b/Misaki.HighPerformance.Unsafe/Helpers/HashMapHelper.cs index 40dbb7f..2aa61ef 100644 --- a/Misaki.HighPerformance.Unsafe/Helpers/HashMapHelper.cs +++ b/Misaki.HighPerformance.Unsafe/Helpers/HashMapHelper.cs @@ -128,7 +128,7 @@ public unsafe struct HashMapHelper : IDisposable return totalSize; } - public HashMapHelper(int capacity, int sizeOfTValue, uint minGrowth, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) + public HashMapHelper(int capacity, int sizeOfTValue, uint minGrowth, Allocator allocator, AllocationOption allocationOption) { if (capacity <= 0) { diff --git a/Misaki.HighPerformance.Unsafe/Services/AllocationManager.cs b/Misaki.HighPerformance.Unsafe/Services/AllocationManager.cs index ecff71b..a587f53 100644 --- a/Misaki.HighPerformance.Unsafe/Services/AllocationManager.cs +++ b/Misaki.HighPerformance.Unsafe/Services/AllocationManager.cs @@ -5,18 +5,12 @@ namespace Misaki.HighPerformance.Unsafe.Services; public static unsafe class AllocationManager { - private readonly struct AllocationInfo(void* ptr, nuint size) - { - public readonly void* ptr = ptr; - public readonly nuint size = size; - } - private const uint _DEFAULT_ARENA_SIZE = 512 * 1024; // 512 KB private static DynamicArena _arena; private static bool _initialized; - private static UnsafeQueue _allocated; + private static UnsafeHashMap _allocated; private static readonly Lock _lock = new(); @@ -32,7 +26,7 @@ public static unsafe class AllocationManager } _arena = new DynamicArena(initialSize); - _allocated = new UnsafeQueue(32, Allocator.Persistent, AllocationOption.UnTracked); + _allocated = new(32, Allocator.Persistent, AllocationOption.UnTracked); _initialized = true; } @@ -62,7 +56,7 @@ public static unsafe class AllocationManager case Allocator.Persistent: var allocationSize = size * (nuint)sizeof(T); buffer = (T*)AlignedAlloc(allocationSize, alignSize); - _allocated.Enqueue(new AllocationInfo(buffer, allocationSize)); + _allocated[(IntPtr)buffer] = allocationSize; break; default: @@ -78,6 +72,42 @@ public static unsafe class AllocationManager } } + internal static T* Realloc(T* buffer, uint size, uint alignSize, Allocator allocator) + where T : unmanaged + { + if (!_initialized) + { + throw new InvalidOperationException("The AllocationManager has not been initialized."); + } + + lock (_lock) + { + T* newBuffer; + switch (allocator) + { + case Allocator.Temp: + newBuffer = (T*)_arena.Allocate(size * (uint)sizeof(T), alignSize, AllocationOption.None); + break; + + case Allocator.Persistent: + var allocationSize = size * (nuint)sizeof(T); + newBuffer = (T*)AlignedRealloc(buffer, allocationSize, alignSize); + + // If the allocation map can not find the old value, which means that it's a untracked allocation + if (_allocated.Remove((IntPtr)buffer)) + { + _allocated.Add((IntPtr)newBuffer, allocationSize); + } + break; + + default: + throw new ArgumentOutOfRangeException(nameof(allocator), "Invalid allocator type."); + } + + return newBuffer; + } + } + internal static void Free(void* ptr, Allocator allocator) { lock (_lock) @@ -117,10 +147,10 @@ public static unsafe class AllocationManager _arena.Dispose(); nuint unfreeBytes = 0u; - while (_allocated.TryDequeue(out var allocationInfo)) + foreach (var pair in _allocated) { - unfreeBytes += allocationInfo.size; - AlignedFree(allocationInfo.ptr); + unfreeBytes += pair.Value; + AlignedFree((void*)pair.Key); } _allocated.Dispose();