Added new TempJobAllocator
Added new AllocationHandle property in Stack.Scope. Changed the ref AllocationHandle constructor parameter to AllocationHandle on of all UnsafeCollection types Removed Allocator.Stack. Use Stack.Scope.AllocationHandle to allocate on stack instead.
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
using Misaki.HighPerformance.Collections;
|
||||
using Misaki.HighPerformance.LowLevel.Contracts;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
||||
|
||||
public readonly struct MemoryHandle
|
||||
public readonly struct MemoryHandle : IEquatable<MemoryHandle>
|
||||
{
|
||||
public readonly int id;
|
||||
public readonly int generation;
|
||||
@@ -20,6 +20,36 @@ public readonly struct MemoryHandle
|
||||
this.id = id;
|
||||
this.generation = generation;
|
||||
}
|
||||
|
||||
public bool Equals(MemoryHandle other)
|
||||
{
|
||||
return id == other.id && generation == other.generation;
|
||||
}
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||
{
|
||||
return obj is MemoryHandle other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return id ^ generation;
|
||||
}
|
||||
|
||||
public override string? ToString()
|
||||
{
|
||||
return $"MemoryHandle(Id: {id}, Generation: {generation})";
|
||||
}
|
||||
|
||||
public static bool operator ==(MemoryHandle left, MemoryHandle right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(MemoryHandle left, MemoryHandle right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,7 +96,7 @@ public static unsafe class AllocationManager
|
||||
private DynamicArena _arena;
|
||||
private AllocationHandle _handle;
|
||||
|
||||
public readonly ref AllocationHandle Handle => ref Unsafe.AsRef(in _handle);
|
||||
public readonly AllocationHandle Handle => _handle;
|
||||
|
||||
public void Init(uint initialSize)
|
||||
{
|
||||
@@ -84,7 +114,7 @@ public static unsafe class AllocationManager
|
||||
return null;
|
||||
}
|
||||
|
||||
*pHandle = AddAllocation((IntPtr)ptr);
|
||||
*pHandle = GetMagicHandle();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -103,16 +133,13 @@ public static unsafe class AllocationManager
|
||||
}
|
||||
|
||||
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||
RemoveAllocation(*pHandle);
|
||||
|
||||
*pHandle = AddAllocation((IntPtr)newPtr);
|
||||
return newPtr;
|
||||
}
|
||||
|
||||
private static void Free(void* instance, void* ptr, MemoryHandle pHandle)
|
||||
private static void Free(void* instance, void* ptr, MemoryHandle handle)
|
||||
{
|
||||
// The arena allocator does not free individual blocks, as it manages memory in chunks.
|
||||
s_allocations.Remove(pHandle.id, pHandle.generation);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
@@ -130,7 +157,7 @@ public static unsafe class AllocationManager
|
||||
{
|
||||
private AllocationHandle _handle;
|
||||
|
||||
public readonly ref AllocationHandle Handle => ref Unsafe.AsRef(in _handle);
|
||||
public readonly AllocationHandle Handle => _handle;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
@@ -177,11 +204,11 @@ public static unsafe class AllocationManager
|
||||
private static Stack s_stack;
|
||||
private AllocationHandle _handle;
|
||||
|
||||
public readonly ref AllocationHandle Handle => ref Unsafe.AsRef(in _handle);
|
||||
public readonly AllocationHandle Handle => _handle;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
_handle = new(Unsafe.AsPointer(ref this), &Allocate, &Reallocate, &FreeBlock);
|
||||
_handle = new(Unsafe.AsPointer(ref this), &Allocate, &Reallocate, &Free);
|
||||
}
|
||||
|
||||
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
||||
@@ -193,7 +220,7 @@ public static unsafe class AllocationManager
|
||||
return null;
|
||||
}
|
||||
|
||||
*pHandle = AddAllocation((IntPtr)ptr);
|
||||
*pHandle = GetMagicHandle();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -211,24 +238,21 @@ public static unsafe class AllocationManager
|
||||
}
|
||||
|
||||
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||
RemoveAllocation(*pHandle);
|
||||
|
||||
*pHandle = AddAllocation((IntPtr)newPtr);
|
||||
return newPtr;
|
||||
}
|
||||
|
||||
private static void FreeBlock(void* instance, void* ptr, MemoryHandle pHandle)
|
||||
private static void Free(void* instance, void* ptr, MemoryHandle handle)
|
||||
{
|
||||
s_allocations.Remove(pHandle.id, pHandle.generation);
|
||||
}
|
||||
|
||||
public static Stack.Scope CreateScope()
|
||||
public static Stack.Scope CreateScope(StackAllocator* pSelf)
|
||||
{
|
||||
return s_stack.CreateScope();
|
||||
return s_stack.CreateScope(pSelf->_handle);
|
||||
}
|
||||
}
|
||||
|
||||
private const uint _DEFAULT_MEMORY_POOL_SIZE = 512 * 1024; // 512 KB
|
||||
private const uint _DEFAULT_MEMORY_POOL_SIZE = 1024 * 1024; // 1 MB
|
||||
|
||||
private static readonly ArenaAllocator* s_pArenaAllocator;
|
||||
private static readonly HeapAllocator* s_pHeapAllocator;
|
||||
@@ -247,6 +271,11 @@ public static unsafe class AllocationManager
|
||||
/// </summary>
|
||||
public static int LiveAllocationCount => s_allocations.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the debug layer is currently enabled.
|
||||
/// </summary>
|
||||
public static bool IsDebugLayerEnabled => s_debugLayer;
|
||||
|
||||
static AllocationManager()
|
||||
{
|
||||
s_pArenaAllocator = (ArenaAllocator*)NativeMemory.Alloc((nuint)sizeof(ArenaAllocator));
|
||||
@@ -432,16 +461,14 @@ public static unsafe class AllocationManager
|
||||
/// <returns>A reference to the allocation pHandle associated with the specified allocator type.</returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref AllocationHandle GetAllocationHandle(Allocator allocator)
|
||||
public static AllocationHandle GetAllocationHandle(Allocator allocator)
|
||||
{
|
||||
switch (allocator)
|
||||
{
|
||||
case Allocator.Temp:
|
||||
return ref s_pArenaAllocator->Handle;
|
||||
return s_pArenaAllocator->Handle;
|
||||
case Allocator.Persistent:
|
||||
return ref s_pHeapAllocator->Handle;
|
||||
case Allocator.Stack:
|
||||
return ref s_pStackAllocator->Handle;
|
||||
return s_pHeapAllocator->Handle;
|
||||
default:
|
||||
throw new ArgumentException("Target allocator type does not support custom allocation.", nameof(allocator));
|
||||
}
|
||||
@@ -489,6 +516,7 @@ public static unsafe class AllocationManager
|
||||
/// </summary>
|
||||
/// <param name="ptr">A pointer to the memory block to be freed. The pointer must have been returned by a compatible heap allocation
|
||||
/// method and must not be null.</param>
|
||||
/// <param name="handle">The handle representing the memory allocation to free. The handle must be valid and previously allocated.</param>
|
||||
public static void HeapFree(void* ptr, MemoryHandle handle)
|
||||
{
|
||||
if (s_debugLayer)
|
||||
@@ -503,6 +531,18 @@ public static unsafe class AllocationManager
|
||||
RemoveAllocation(handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases a block of unmanaged memory previously allocated by the heap allocator.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle representing the memory allocation to free. The handle must be valid and previously allocated.</param>
|
||||
public static void HeapFree(MemoryHandle handle)
|
||||
{
|
||||
if (TryGetAllocation(handle, out var ptr))
|
||||
{
|
||||
HeapFree((void*)ptr, handle);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the temporary memory allocator, clearing all allocated memory.
|
||||
/// </summary>
|
||||
@@ -519,7 +559,7 @@ public static unsafe class AllocationManager
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Stack.Scope CreateStackScope()
|
||||
{
|
||||
return StackAllocator.CreateScope();
|
||||
return StackAllocator.CreateScope(s_pStackAllocator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -534,6 +574,12 @@ public static unsafe class AllocationManager
|
||||
return new MemoryHandle(id, generation);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static MemoryHandle GetMagicHandle()
|
||||
{
|
||||
return new MemoryHandle(int.MinValue, int.MinValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the memory allocation associated with the specified handle.
|
||||
/// </summary>
|
||||
@@ -565,6 +611,12 @@ public static unsafe class AllocationManager
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool ContainsAllocation(MemoryHandle handle)
|
||||
{
|
||||
if (handle.id == int.MinValue && handle.generation == int.MinValue)
|
||||
{
|
||||
// Magic handle always valid
|
||||
return true;
|
||||
}
|
||||
|
||||
return s_allocations.Contains(handle.id, handle.generation);
|
||||
}
|
||||
|
||||
@@ -619,7 +671,7 @@ public static unsafe class AllocationManager
|
||||
throw new MemoryLeakException(CollectionsMarshal.AsSpan(snapshot));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (LiveAllocationCount != 0)
|
||||
{
|
||||
throw new MemoryLeakException($"Found {LiveAllocationCount} memory lakes! Please enable debug layer for more informations.");
|
||||
@@ -643,4 +695,4 @@ public static unsafe class AllocationManager
|
||||
|
||||
s_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,4 @@ public enum Allocator : byte
|
||||
/// Allocator for persistent allocations. Allocations are not automatically released after use.
|
||||
/// </summary>
|
||||
Persistent,
|
||||
/// <summary>
|
||||
/// Allocator for stack allocations. Must have at least one active stack scope. Allocations are automatically released when the stack scope is exited.
|
||||
/// </summary>
|
||||
Stack
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ public unsafe struct DynamicArena : IDisposable
|
||||
_root->arena = new Arena(initialSize);
|
||||
_root->next = null;
|
||||
_current = _root;
|
||||
|
||||
_nodeCreationLock = 0;
|
||||
}
|
||||
|
||||
private bool TryCreateNewNode(nuint size)
|
||||
@@ -165,4 +167,4 @@ public unsafe struct DynamicArena : IDisposable
|
||||
_root = null;
|
||||
_current = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
73
Misaki.HighPerformance.LowLevel/Buffer/IAllocator.cs
Normal file
73
Misaki.HighPerformance.LowLevel/Buffer/IAllocator.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
||||
|
||||
/// <summary>
|
||||
/// A structure that encapsulates function pointers for memory allocation operations.
|
||||
/// </summary>
|
||||
public readonly unsafe struct AllocationHandle
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a pointer to the allocator instance associated with this allocation handle.
|
||||
/// </summary>
|
||||
public void* pAllocator
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a function pointer for allocating memory.
|
||||
/// </summary>
|
||||
public AllocFunc Alloc
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a function pointer for reallocating memory.
|
||||
/// </summary>
|
||||
public ReallocFunc Realloc
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a function pointer for freeing allocated memory.
|
||||
/// </summary>
|
||||
public FreeFunc Free
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AllocationHandle"/> struct with the specified allocator and memory
|
||||
/// management functions.
|
||||
/// </summary>
|
||||
/// <param name="allocator">A pointer to the allocator instance used for memory management.</param>
|
||||
/// <param name="alloc">The function used to allocate memory.</param>
|
||||
/// <param name="realloc">The function used to reallocate memory.</param>
|
||||
/// <param name="free">The function used to free allocated memory.</param>
|
||||
public AllocationHandle(void* allocator, AllocFunc alloc, ReallocFunc realloc, FreeFunc free)
|
||||
{
|
||||
pAllocator = allocator;
|
||||
Alloc = alloc;
|
||||
Realloc = realloc;
|
||||
Free = free;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an allocator interface for managing memory allocations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The allocator must be pined to a specific memory region.
|
||||
/// Otherwise the reference of the <see cref="AllocationHandle.pAllocator"/>, may become invalid and lead to undefined behavior.
|
||||
/// </remarks>
|
||||
public interface IAllocator
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a reference to the allocation handle associated with this allocator.
|
||||
/// </summary>
|
||||
AllocationHandle Handle
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,15 @@ public unsafe struct Stack : IDisposable
|
||||
public readonly ref struct Scope : IDisposable
|
||||
{
|
||||
private readonly Stack* _allocator;
|
||||
private readonly AllocationHandle _handle;
|
||||
private readonly nuint _originalOffset;
|
||||
|
||||
internal Scope(Stack* allocator)
|
||||
public readonly AllocationHandle AllocationHandle => _handle;
|
||||
|
||||
internal Scope(Stack* allocator, AllocationHandle handle)
|
||||
{
|
||||
_allocator = allocator;
|
||||
_handle = handle;
|
||||
_originalOffset = allocator->_offset;
|
||||
_allocator->_activeScopeCount++;
|
||||
}
|
||||
@@ -84,12 +88,15 @@ public unsafe struct Stack : IDisposable
|
||||
/// <summary>
|
||||
/// Creates a new scope instance associated with the current stack context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The instance of <see cref="Stack"/> must be pinned or allocated on the native heap to ensure that the pointer remains valid for the lifetime of the scope.
|
||||
/// </remarks>
|
||||
/// <returns>A <see cref="Scope"/> object that represents a scope tied to this stack.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Scope CreateScope()
|
||||
public Scope CreateScope(AllocationHandle handle)
|
||||
{
|
||||
EnsureInitialize();
|
||||
return new Scope((Stack*)Unsafe.AsPointer(ref this));
|
||||
return new Scope((Stack*)Unsafe.AsPointer(ref this), handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -152,4 +159,4 @@ public unsafe struct Stack : IDisposable
|
||||
_size = 0;
|
||||
_offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user