backup
This commit is contained in:
@@ -11,7 +11,7 @@ namespace Misaki.HighPerformance.Image;
|
|||||||
/// <remarks>The image data is stored as a contiguous block of unmanaged memory and must be released by calling
|
/// <remarks>The image data is stored as a contiguous block of unmanaged memory and must be released by calling
|
||||||
/// <see cref="Dispose"/> when no longer needed. Be careful that this struct won't stop your double free if you copy it.
|
/// <see cref="Dispose"/> when no longer needed. Be careful that this struct won't stop your double free if you copy it.
|
||||||
/// Ensure to have proper ownership management when using this struct.</remarks>
|
/// Ensure to have proper ownership management when using this struct.</remarks>
|
||||||
public unsafe readonly struct ImageResult : IDisposable
|
public readonly unsafe struct ImageResult : IDisposable
|
||||||
{
|
{
|
||||||
public byte* Data
|
public byte* Data
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Misaki.HighPerformance.Image;
|
|||||||
/// <remarks>The image data is stored as a contiguous block of unmanaged memory and must be released by calling
|
/// <remarks>The image data is stored as a contiguous block of unmanaged memory and must be released by calling
|
||||||
/// <see cref="Dispose"/> when no longer needed. Be careful that this struct won't stop your double free if you copy it.
|
/// <see cref="Dispose"/> when no longer needed. Be careful that this struct won't stop your double free if you copy it.
|
||||||
/// Ensure to have proper ownership management when using this struct.</remarks>
|
/// Ensure to have proper ownership management when using this struct.</remarks>
|
||||||
public unsafe readonly struct ImageResultFloat : IDisposable
|
public readonly unsafe struct ImageResultFloat : IDisposable
|
||||||
{
|
{
|
||||||
public float* Data
|
public float* Data
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Jobs;
|
namespace Misaki.HighPerformance.Jobs;
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ public unsafe struct TempJobAllocator : IAllocator, IDisposable
|
|||||||
_currentFrameIndex = 0;
|
_currentFrameIndex = 0;
|
||||||
_memoryHandle = memoryHandle;
|
_memoryHandle = memoryHandle;
|
||||||
|
|
||||||
for (int i = 0; i < _FRAME_LATENCY; i++)
|
for (var i = 0; i < _FRAME_LATENCY; i++)
|
||||||
{
|
{
|
||||||
_pArena[i] = new VirtualArena(capacity);
|
_pArena[i] = new VirtualArena(capacity);
|
||||||
_allocationsPerFrame[i] = 0;
|
_allocationsPerFrame[i] = 0;
|
||||||
@@ -75,7 +75,7 @@ public unsafe struct TempJobAllocator : IAllocator, IDisposable
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryUtility.MemCpy(ptr, newPtr,Math.Min(oldSize, newSize));
|
MemoryUtility.MemCpy(ptr, newPtr, Math.Min(oldSize, newSize));
|
||||||
|
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ public unsafe struct TempJobAllocator : IAllocator, IDisposable
|
|||||||
private static bool IsValid(void* instance, MemoryHandle handle)
|
private static bool IsValid(void* instance, MemoryHandle handle)
|
||||||
{
|
{
|
||||||
var pSelf = (TempJobAllocator*)instance;
|
var pSelf = (TempJobAllocator*)instance;
|
||||||
return handle.id == _MAGIC_ID && handle.generation > pSelf->_currentFrameCount - _FRAME_LATENCY;
|
return handle.ID == _MAGIC_ID && handle.Generation > pSelf->_currentFrameCount - _FRAME_LATENCY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int AdvanceFrame()
|
public int AdvanceFrame()
|
||||||
@@ -107,7 +107,7 @@ public unsafe struct TempJobAllocator : IAllocator, IDisposable
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _FRAME_LATENCY; i++)
|
for (var i = 0; i < _FRAME_LATENCY; i++)
|
||||||
{
|
{
|
||||||
_pArena[i].Dispose();
|
_pArena[i].Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Jobs;
|
namespace Misaki.HighPerformance.Jobs;
|
||||||
|
|||||||
@@ -1,6 +1,22 @@
|
|||||||
global using static Misaki.HighPerformance.LowLevel.Utilities.MemoryUtility;
|
global using static Misaki.HighPerformance.LowLevel.Utilities.MemoryUtility;
|
||||||
|
|
||||||
global using unsafe AllocFunc = delegate*<void*, nuint, nuint, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle*, void*>;
|
global using unsafe AllocFunc = delegate*<void*, nuint, nuint, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption
|
||||||
global using unsafe ReallocFunc = delegate*<void*, void*, nuint, nuint, nuint, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle*, void*>;
|
#if ENABLE_SAFETY_CHECKS
|
||||||
global using unsafe FreeFunc = delegate*<void*, void*, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle, void>;
|
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle*
|
||||||
global using unsafe IsValidFunc = delegate*<void*, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle, bool>;
|
#endif
|
||||||
|
, void*>;
|
||||||
|
global using unsafe ReallocFunc = delegate*<void*, void*, nuint, nuint, nuint, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle*
|
||||||
|
#endif
|
||||||
|
, void*>;
|
||||||
|
global using unsafe FreeFunc = delegate*<void*, void*
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle
|
||||||
|
#endif
|
||||||
|
, void>;
|
||||||
|
global using unsafe IsValidFunc = delegate*<void*
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, Misaki.HighPerformance.LowLevel.Buffer.MemoryHandle
|
||||||
|
#endif
|
||||||
|
, bool>;
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
#define ENABLE_DEBUG_LAYER
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
using Misaki.HighPerformance.Collections;
|
using Misaki.HighPerformance.Collections;
|
||||||
|
#endif
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if ENABLE_DEBUG_LAYER
|
||||||
@@ -13,20 +17,30 @@ namespace Misaki.HighPerformance.LowLevel.Buffer;
|
|||||||
public readonly struct AllocationInfo
|
public readonly struct AllocationInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the size of the allocation in bytes.
|
/// Gets the address of the allocated memory block.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr Address
|
||||||
|
{
|
||||||
|
get; init;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of the allocation in bytes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public nuint Size
|
public nuint Size
|
||||||
{
|
{
|
||||||
get; init;
|
get; init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the stack trace at the time of allocation for debugging purposes.
|
/// Gets the stack trace at the time of allocation for debugging purposes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StackTrace StackTrace
|
public GCHandle StackTrace
|
||||||
{
|
{
|
||||||
get; init;
|
get; init;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct AllocationManagerInitOpts
|
public readonly struct AllocationManagerInitOpts
|
||||||
@@ -85,31 +99,51 @@ public static unsafe class AllocationManager
|
|||||||
Alloc = &Allocate,
|
Alloc = &Allocate,
|
||||||
Realloc = &Reallocate,
|
Realloc = &Reallocate,
|
||||||
Free = null,
|
Free = null,
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
IsValid = &IsValid
|
IsValid = &IsValid
|
||||||
|
#else
|
||||||
|
IsValid = null
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
_currentTick = 0;
|
_currentTick = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var selfPtr = (ArenaAllocator*)instance;
|
var selfPtr = (ArenaAllocator*)instance;
|
||||||
var ptr = selfPtr->_arena.Allocate(size, alignment, allocationOption);
|
var ptr = selfPtr->_arena.Allocate(size, alignment, allocationOption);
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = MemoryHandle.Invalid;
|
*pHandle = MemoryHandle.Invalid;
|
||||||
|
#endif
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_ARENA_MAGIC_ID, selfPtr->_currentTick);
|
*pHandle = new MemoryHandle(_ARENA_MAGIC_ID, selfPtr->_currentTick);
|
||||||
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return Allocate(instance, newSize, alignment, allocationOption, pHandle);
|
return Allocate(instance, newSize, alignment, allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, pHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var selfPtr = (ArenaAllocator*)instance;
|
var selfPtr = (ArenaAllocator*)instance;
|
||||||
@@ -121,15 +155,19 @@ public static unsafe class AllocationManager
|
|||||||
|
|
||||||
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_ARENA_MAGIC_ID, selfPtr->_currentTick);
|
*pHandle = new MemoryHandle(_ARENA_MAGIC_ID, selfPtr->_currentTick);
|
||||||
|
#endif
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
private static bool IsValid(void* instance, MemoryHandle handle)
|
private static bool IsValid(void* instance, MemoryHandle handle)
|
||||||
{
|
{
|
||||||
var selfPtr = (ArenaAllocator*)instance;
|
var selfPtr = (ArenaAllocator*)instance;
|
||||||
return handle.id == _ARENA_MAGIC_ID && handle.generation == selfPtr->_currentTick;
|
return handle.ID == _ARENA_MAGIC_ID && handle.Generation == selfPtr->_currentTick;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
@@ -157,20 +195,42 @@ public static unsafe class AllocationManager
|
|||||||
Alloc = &Allocate,
|
Alloc = &Allocate,
|
||||||
Realloc = &Reallocate,
|
Realloc = &Reallocate,
|
||||||
Free = &Free,
|
Free = &Free,
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
IsValid = &IsValid
|
IsValid = &IsValid
|
||||||
|
#else
|
||||||
|
IsValid = null
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Allocate(void* _, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Allocate(void* _, nuint size, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return HeapAlloc(size, alignment, allocationOption, pHandle);
|
return HeapAlloc(size, alignment, allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, pHandle
|
||||||
|
#else
|
||||||
|
, default
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Reallocate(void* _, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Reallocate(void* _, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return Allocate(null, newSize, alignment, allocationOption, pHandle);
|
return Allocate(null, newSize, alignment, allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, pHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryHandle newHandle;
|
MemoryHandle newHandle;
|
||||||
@@ -181,21 +241,41 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||||
HeapFree(ptr, *pHandle);
|
HeapFree(ptr
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, *pHandle
|
||||||
|
#else
|
||||||
|
, default
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = newHandle;
|
*pHandle = newHandle;
|
||||||
|
#endif
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Free(void* _, void* ptr, MemoryHandle handle)
|
private static void Free(void* _, void* ptr
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle handle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
HeapFree(ptr, handle);
|
HeapFree(ptr
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, handle
|
||||||
|
#else
|
||||||
|
, default
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
private static bool IsValid(void* _, MemoryHandle handle)
|
private static bool IsValid(void* _, MemoryHandle handle)
|
||||||
{
|
{
|
||||||
return ContainsAllocation(handle);
|
return ContainsAllocation(handle);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct StackAllocator : IAllocator, IDisposable
|
private struct StackAllocator : IAllocator, IDisposable
|
||||||
@@ -221,7 +301,11 @@ public static unsafe class AllocationManager
|
|||||||
Alloc = &Allocate,
|
Alloc = &Allocate,
|
||||||
Realloc = &Reallocate,
|
Realloc = &Reallocate,
|
||||||
Free = null,
|
Free = null,
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
IsValid = &IsValid
|
IsValid = &IsValid
|
||||||
|
#else
|
||||||
|
IsValid = null
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,26 +348,42 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
EnsureInitialize();
|
EnsureInitialize();
|
||||||
|
|
||||||
var ptr = s_stack.Allocate(size, alignment, allocationOption);
|
var ptr = s_stack.Allocate(size, alignment, allocationOption);
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = MemoryHandle.Invalid;
|
*pHandle = MemoryHandle.Invalid;
|
||||||
|
#endif
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
||||||
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return Allocate(instance, newSize, alignment, allocationOption, pHandle);
|
return Allocate(instance, newSize, alignment, allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, pHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureInitialize();
|
EnsureInitialize();
|
||||||
@@ -302,7 +402,9 @@ public static unsafe class AllocationManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
||||||
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,14 +416,18 @@ public static unsafe class AllocationManager
|
|||||||
|
|
||||||
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
*pHandle = new MemoryHandle(_STACK_MAGIC_ID, (int)s_stack.Offset);
|
||||||
|
#endif
|
||||||
return newPtr;
|
return newPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
private static bool IsValid(void* instance, MemoryHandle handle)
|
private static bool IsValid(void* instance, MemoryHandle handle)
|
||||||
{
|
{
|
||||||
return handle.id == _STACK_MAGIC_ID && handle.generation <= (int)s_stack.Offset;
|
return handle.ID == _STACK_MAGIC_ID && handle.Generation <= (int)s_stack.Offset;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public static VirtualStack.Scope CreateScope(StackAllocator* pSelf)
|
public static VirtualStack.Scope CreateScope(StackAllocator* pSelf)
|
||||||
{
|
{
|
||||||
@@ -359,11 +465,19 @@ public static unsafe class AllocationManager
|
|||||||
Alloc = &Allocate,
|
Alloc = &Allocate,
|
||||||
Realloc = &Reallocate,
|
Realloc = &Reallocate,
|
||||||
Free = &Free,
|
Free = &Free,
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
IsValid = &IsValid
|
IsValid = &IsValid
|
||||||
|
#else
|
||||||
|
IsValid = null
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Allocate(void* instance, nuint size, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var selfPtr = (FreeListAllocator*)instance;
|
var selfPtr = (FreeListAllocator*)instance;
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if ENABLE_DEBUG_LAYER
|
||||||
@@ -390,26 +504,35 @@ public static unsafe class AllocationManager
|
|||||||
MemClear(user, size);
|
MemClear(user, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
*pHandle = AddAllocation(user);
|
*pHandle = AddAllocation(user, size);
|
||||||
return user;
|
return user;
|
||||||
#else
|
#else
|
||||||
var ptr = selfPtr->_freeList.Allocate(size, alignment, allocationOption);
|
var ptr = selfPtr->_freeList.Allocate(size, alignment, allocationOption);
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
*pHandle = MemoryHandle.Invalid;
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
*pHandle = AddAllocation(ptr);
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
*pHandle = AddAllocation(ptr, size);
|
||||||
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption, MemoryHandle* pHandle)
|
private static void* Reallocate(void* instance, void* ptr, nuint oldSize, nuint newSize, nuint alignment, AllocationOption allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle* pHandle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (ptr == null)
|
if (ptr == null)
|
||||||
{
|
{
|
||||||
return Allocate(instance, newSize, alignment, allocationOption, pHandle);
|
return Allocate(instance, newSize, alignment, allocationOption
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, pHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var selfPtr = (FreeListAllocator*)instance;
|
var selfPtr = (FreeListAllocator*)instance;
|
||||||
@@ -445,7 +568,7 @@ public static unsafe class AllocationManager
|
|||||||
selfPtr->_freeList.Free(oldHeader->basePtr);
|
selfPtr->_freeList.Free(oldHeader->basePtr);
|
||||||
RemoveAllocation(*pHandle);
|
RemoveAllocation(*pHandle);
|
||||||
|
|
||||||
*pHandle = AddAllocation(newUser);
|
*pHandle = AddAllocation(newUser, newSize);
|
||||||
return newUser;
|
return newUser;
|
||||||
#else
|
#else
|
||||||
var newPtr = selfPtr->_freeList.Allocate(newSize, alignment, allocationOption);
|
var newPtr = selfPtr->_freeList.Allocate(newSize, alignment, allocationOption);
|
||||||
@@ -457,19 +580,28 @@ public static unsafe class AllocationManager
|
|||||||
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
MemCpy(newPtr, ptr, Math.Min(oldSize, newSize));
|
||||||
|
|
||||||
selfPtr->_freeList.Free(ptr);
|
selfPtr->_freeList.Free(ptr);
|
||||||
RemoveAllocation(*pHandle);
|
|
||||||
|
|
||||||
*pHandle = AddAllocation(newPtr);
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
RemoveAllocation(*pHandle);
|
||||||
|
*pHandle = AddAllocation(newPtr, newSize);
|
||||||
|
#endif
|
||||||
|
|
||||||
return newPtr;
|
return newPtr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
private static bool IsValid(void* instance, MemoryHandle handle)
|
private static bool IsValid(void* instance, MemoryHandle handle)
|
||||||
{
|
{
|
||||||
return ContainsAllocation(handle);
|
return ContainsAllocation(handle);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private static void Free(void* instance, void* ptr, MemoryHandle handle)
|
private static void Free(void* instance, void* ptr
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
, MemoryHandle handle
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var selfPtr = (FreeListAllocator*)instance;
|
var selfPtr = (FreeListAllocator*)instance;
|
||||||
#if ENABLE_DEBUG_LAYER
|
#if ENABLE_DEBUG_LAYER
|
||||||
@@ -477,10 +609,13 @@ public static unsafe class AllocationManager
|
|||||||
UnlinkHeader(header);
|
UnlinkHeader(header);
|
||||||
HeaderFreeHandle(header);
|
HeaderFreeHandle(header);
|
||||||
selfPtr->_freeList.Free(header->basePtr);
|
selfPtr->_freeList.Free(header->basePtr);
|
||||||
|
RemoveAllocation(handle);
|
||||||
#else
|
#else
|
||||||
selfPtr->_freeList.Free(ptr);
|
selfPtr->_freeList.Free(ptr);
|
||||||
#endif
|
#if ENABLE_SAFETY_CHECKS
|
||||||
RemoveAllocation(handle);
|
RemoveAllocation(handle);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -499,17 +634,18 @@ public static unsafe class AllocationManager
|
|||||||
private static AllocationHeader* s_pLiveHead;
|
private static AllocationHeader* s_pLiveHead;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private static ConcurrentSlotMap<IntPtr> s_allocations = null!;
|
#if ENABLE_SAFETY_CHECKS
|
||||||
|
private static ConcurrentSlotMap<AllocationInfo> s_allocations = null!;
|
||||||
private static bool s_initialized;
|
|
||||||
private static nuint s_threadLocalStackDefaultSize;
|
|
||||||
|
|
||||||
public static readonly MemoryHandle MagicHandle = new MemoryHandle(int.MinValue, int.MinValue);
|
public static readonly MemoryHandle MagicHandle = new MemoryHandle(int.MinValue, int.MinValue);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the number of live tracked heap allocations.
|
/// Gets the number of live tracked heap allocations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static int LiveAllocationCount => s_allocations.Count;
|
public static int LiveAllocationCount => s_allocations.Count;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private static bool s_initialized;
|
||||||
|
private static nuint s_threadLocalStackDefaultSize;
|
||||||
|
|
||||||
public static void Initialize(AllocationManagerInitOpts opts)
|
public static void Initialize(AllocationManagerInitOpts opts)
|
||||||
{
|
{
|
||||||
@@ -522,8 +658,9 @@ public static unsafe class AllocationManager
|
|||||||
s_liveLock = new SpinLock(false);
|
s_liveLock = new SpinLock(false);
|
||||||
s_pLiveHead = null;
|
s_pLiveHead = null;
|
||||||
#endif
|
#endif
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
s_allocations = new ConcurrentSlotMap<IntPtr>(256);
|
s_allocations = new ConcurrentSlotMap<AllocationInfo>(256);
|
||||||
|
#endif
|
||||||
|
|
||||||
var ptr = (byte*)Malloc((nuint)(sizeof(ArenaAllocator) + sizeof(HeapAllocator) + sizeof(StackAllocator) + sizeof(FreeListAllocator)));
|
var ptr = (byte*)Malloc((nuint)(sizeof(ArenaAllocator) + sizeof(HeapAllocator) + sizeof(StackAllocator) + sizeof(FreeListAllocator)));
|
||||||
|
|
||||||
@@ -747,7 +884,7 @@ public static unsafe class AllocationManager
|
|||||||
MemClear(ptr, size);
|
MemClear(ptr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
*pHandle = AddAllocation(ptr);
|
*pHandle = AddAllocation(ptr, size);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -772,7 +909,9 @@ public static unsafe class AllocationManager
|
|||||||
AlignedFree(ptr);
|
AlignedFree(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
RemoveAllocation(handle);
|
RemoveAllocation(handle);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -813,40 +952,71 @@ public static unsafe class AllocationManager
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a memory allocation and returns a handle that can be used to manage or reference the allocated memory.
|
/// Registers a memory allocation and returns a handle that can be used to manage or reference the allocated memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Always returns an invalid handle if ENABLE_SAFETY_CHECKS is disabled.
|
||||||
|
/// </remarks>
|
||||||
/// <param name="ptr">A pointer to the memory block to be registered. The pointer must reference a valid, allocated memory region.</param>
|
/// <param name="ptr">A pointer to the memory block to be registered. The pointer must reference a valid, allocated memory region.</param>
|
||||||
/// <returns>A MemoryHandle representing the registered allocation.</returns>
|
/// <returns>A MemoryHandle representing the registered allocation.</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static MemoryHandle AddAllocation(void* ptr)
|
public static MemoryHandle AddAllocation(void* ptr, nuint size)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
|
|
||||||
var id = s_allocations.Add((nint)ptr, out var generation);
|
var info = new AllocationInfo
|
||||||
|
{
|
||||||
|
Address = (IntPtr)ptr,
|
||||||
|
Size = size,
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
|
StackTrace = GCHandle.Alloc(new StackTrace(1, true))
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
var id = s_allocations.Add(info, out var generation);
|
||||||
return new MemoryHandle(id, generation);
|
return new MemoryHandle(id, generation);
|
||||||
|
#else
|
||||||
|
return MemoryHandle.Invalid;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes the memory allocation associated with the specified handle.
|
/// Removes the memory allocation associated with the specified handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Always returns false if debug layer is disabled.
|
||||||
|
/// </remarks>
|
||||||
/// <param name="handle">The handle representing the memory allocation to remove. The handle must be valid and previously allocated.</param>
|
/// <param name="handle">The handle representing the memory allocation to remove. The handle must be valid and previously allocated.</param>
|
||||||
/// <returns>true if the allocation was successfully removed; otherwise, false.</returns>
|
/// <returns>true if the allocation was successfully removed; otherwise, false.</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool RemoveAllocation(MemoryHandle handle)
|
public static bool RemoveAllocation(MemoryHandle handle)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
return s_allocations.Remove(handle.id, handle.generation);
|
return s_allocations.Remove(handle.ID, handle.Generation);
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to retrieve the memory allocation pointer associated with the specified handle.
|
/// Attempts to retrieve the memory allocation pointer associated with the specified handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Always returns false if debug layer is disabled, and the output pointer will be set to <see cref="IntPtr.Zero"/>.
|
||||||
|
/// </remarks>
|
||||||
/// <param name="handle">The memory handle identifying the allocation to retrieve allocation.</param>
|
/// <param name="handle">The memory handle identifying the allocation to retrieve allocation.</param>
|
||||||
/// <param name="ptr">When this method returns, contains the pointer to the memory allocation if found; otherwise, <see cref="IntPtr.Zero"/>.</param>
|
/// <param name="ptr">When this method returns, contains the pointer to the memory allocation if found; otherwise, <see cref="IntPtr.Zero"/>.</param>
|
||||||
/// <returns>true if the allocation was found and <paramref name="ptr"/> contains a valid pointer; otherwise, false.</returns>
|
/// <returns>true if the allocation was found and <paramref name="ptr"/> contains a valid pointer; otherwise, false.</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool TryGetAllocation(MemoryHandle handle, out IntPtr ptr)
|
public static bool TryGetAllocation(MemoryHandle handle, out IntPtr ptr)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
return s_allocations.TryGetElement(handle.id, handle.generation, out ptr);
|
return s_allocations.TryGetElement(handle.ID, handle.Generation, out ptr);
|
||||||
|
#else
|
||||||
|
ptr = IntPtr.Zero;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -855,12 +1025,14 @@ public static unsafe class AllocationManager
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This only validates the memory when you added the allocation via <see cref="AddAllocation(IntPtr)"/>.
|
/// This only validates the memory when you added the allocation via <see cref="AddAllocation(IntPtr)"/>.
|
||||||
/// For validating memory from <see cref="AllocationHandle"/>, use <see cref="AllocationHandle.IsValid"/> instead.
|
/// For validating memory from <see cref="AllocationHandle"/>, use <see cref="AllocationHandle.IsValid"/> instead.
|
||||||
|
/// Always returns false if debug layer is disabled.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="handle">The memory handle to check for an associated allocation.</param>
|
/// <param name="handle">The memory handle to check for an associated allocation.</param>
|
||||||
/// <returns>true if the allocation corresponding to the handle exists; otherwise, false.</returns>
|
/// <returns>true if the allocation corresponding to the handle exists; otherwise, false.</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static bool ContainsAllocation(MemoryHandle handle)
|
public static bool ContainsAllocation(MemoryHandle handle)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_SAFETY_CHECKS
|
||||||
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
Debug.Assert(s_initialized, "AllocationManager is not initialized.");
|
||||||
|
|
||||||
if (handle == MagicHandle)
|
if (handle == MagicHandle)
|
||||||
@@ -868,7 +1040,10 @@ public static unsafe class AllocationManager
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return s_allocations.Contains(handle.id, handle.generation);
|
return s_allocations.Contains(handle.ID, handle.Generation);
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -920,12 +1095,12 @@ public static unsafe class AllocationManager
|
|||||||
{
|
{
|
||||||
throw new MemoryLeakException(CollectionsMarshal.AsSpan(snapshot));
|
throw new MemoryLeakException(CollectionsMarshal.AsSpan(snapshot));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Debug.Assert(LiveAllocationCount == 0);
|
#if ENABLE_SAFETY_CHECKS
|
||||||
#else
|
if (s_allocations.Count != 0)
|
||||||
if (LiveAllocationCount != 0)
|
|
||||||
{
|
{
|
||||||
throw new MemoryLeakException($"Found {LiveAllocationCount} memory lakes! Please enable debug layer for more informations.");
|
throw new InvalidOperationException($"There are still {s_allocations.Count} live tracked allocations.");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -4,20 +4,27 @@ namespace Misaki.HighPerformance.LowLevel.Buffer;
|
|||||||
|
|
||||||
public readonly struct MemoryHandle : IEquatable<MemoryHandle>
|
public readonly struct MemoryHandle : IEquatable<MemoryHandle>
|
||||||
{
|
{
|
||||||
public readonly int id;
|
public readonly int ID
|
||||||
public readonly int generation;
|
{
|
||||||
|
get => field - 1;
|
||||||
|
}
|
||||||
|
|
||||||
public readonly static MemoryHandle Invalid = new(-1, -1);
|
public readonly int Generation
|
||||||
|
{
|
||||||
|
get => field - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly MemoryHandle Invalid = default;
|
||||||
|
|
||||||
public MemoryHandle(int id, int generation)
|
public MemoryHandle(int id, int generation)
|
||||||
{
|
{
|
||||||
this.id = id;
|
ID = id + 1;
|
||||||
this.generation = generation;
|
Generation = generation + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(MemoryHandle other)
|
public bool Equals(MemoryHandle other)
|
||||||
{
|
{
|
||||||
return id == other.id && generation == other.generation;
|
return ID == other.ID && Generation == other.Generation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals([NotNullWhen(true)] object? obj)
|
public override bool Equals([NotNullWhen(true)] object? obj)
|
||||||
@@ -27,12 +34,12 @@ public readonly struct MemoryHandle : IEquatable<MemoryHandle>
|
|||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return id ^ generation;
|
return ID ^ Generation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string? ToString()
|
public override string? ToString()
|
||||||
{
|
{
|
||||||
return $"MemoryHandle(Id: {id}, Generation: {generation})";
|
return $"MemoryHandle(Id: {ID}, Generation: {Generation})";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator ==(MemoryHandle left, MemoryHandle right)
|
public static bool operator ==(MemoryHandle left, MemoryHandle right)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
namespace Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ public unsafe interface IUnsafeCollection : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates whether the object has been created. Returns true if the object is created, otherwise false.
|
/// Indicates whether the object has been created. Returns true if the object is created, otherwise false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If ENABLE_DEBUG_LAYER is not defined, this property will only check if the underlying pointer is not null, which may not be sufficient to determine if the collection is fully initialized and ready for use.
|
||||||
|
/// </remarks>
|
||||||
bool IsCreated
|
bool IsCreated
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@@ -78,7 +77,9 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
private readonly int _sizeOfTValue;
|
private readonly int _sizeOfTValue;
|
||||||
private readonly int _log2MinGrowth;
|
private readonly int _log2MinGrowth;
|
||||||
|
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
private MemoryHandle _memoryHandle;
|
private MemoryHandle _memoryHandle;
|
||||||
|
#endif
|
||||||
private AllocationHandle _allocationHandle;
|
private AllocationHandle _allocationHandle;
|
||||||
|
|
||||||
public const int MINIMAL_CAPACITY = 64;
|
public const int MINIMAL_CAPACITY = 64;
|
||||||
@@ -95,6 +96,7 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
if (_buffer != null)
|
if (_buffer != null)
|
||||||
{
|
{
|
||||||
if (_allocationHandle.IsValid != null)
|
if (_allocationHandle.IsValid != null)
|
||||||
@@ -108,6 +110,9 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
#else
|
||||||
|
return _buffer != null;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,14 +256,22 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
throw new InvalidOperationException("Target allocation handle does not support allocation.");
|
throw new InvalidOperationException("Target allocation handle does not support allocation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
MemoryHandle memHandle;
|
MemoryHandle memHandle;
|
||||||
var buf = (byte*)_allocationHandle.Alloc(_allocationHandle.State, (uint)totalSize, (nuint)_alignment, allocationOption, &memHandle);
|
#endif
|
||||||
|
var buf = (byte*)_allocationHandle.Alloc(_allocationHandle.State, (uint)totalSize, (nuint)_alignment, allocationOption
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
|
, &memHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
_buffer = buf;
|
_buffer = buf;
|
||||||
_keys = (TKey*)(_buffer + keyOffset);
|
_keys = (TKey*)(_buffer + keyOffset);
|
||||||
_next = (int*)(_buffer + nextOffset);
|
_next = (int*)(_buffer + nextOffset);
|
||||||
_buckets = (int*)(_buffer + bucketOffset);
|
_buckets = (int*)(_buffer + bucketOffset);
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
_memoryHandle = memHandle;
|
_memoryHandle = memHandle;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResizeExact(int newCapacity, int newBucketCapacity)
|
private void ResizeExact(int newCapacity, int newBucketCapacity)
|
||||||
@@ -271,7 +284,9 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
var oldNext = _next;
|
var oldNext = _next;
|
||||||
var oldBuckets = _buckets;
|
var oldBuckets = _buckets;
|
||||||
var oldBucketCapacity = _bucketCapacity;
|
var oldBucketCapacity = _bucketCapacity;
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
var oldMemoryHandle = _memoryHandle;
|
var oldMemoryHandle = _memoryHandle;
|
||||||
|
#endif
|
||||||
|
|
||||||
AllocateBuffer(totalSize, keyOffset, nextOffset, bucketOffset, AllocationOption.None);
|
AllocateBuffer(totalSize, keyOffset, nextOffset, bucketOffset, AllocationOption.None);
|
||||||
_capacity = newCapacity;
|
_capacity = newCapacity;
|
||||||
@@ -290,7 +305,11 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
|
|
||||||
if (_allocationHandle.Free != null)
|
if (_allocationHandle.Free != null)
|
||||||
{
|
{
|
||||||
_allocationHandle.Free(_allocationHandle.State, oldBuffer, oldMemoryHandle);
|
_allocationHandle.Free(_allocationHandle.State, oldBuffer
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
|
, oldMemoryHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,7 +724,11 @@ public unsafe struct HashMapHelper<TKey> : IDisposable
|
|||||||
|
|
||||||
if (_allocationHandle.Free != null)
|
if (_allocationHandle.Free != null)
|
||||||
{
|
{
|
||||||
_allocationHandle.Free(_allocationHandle.State, _buffer, _memoryHandle);
|
_allocationHandle.Free(_allocationHandle.State, _buffer
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
|
, _memoryHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer = null;
|
_buffer = null;
|
||||||
|
|||||||
@@ -143,4 +143,9 @@ public readonly unsafe struct ReadOnlyUnsafeCollection<T> : IEnumerable<T>
|
|||||||
{
|
{
|
||||||
return _buffer;
|
return _buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static implicit operator ReadOnlySpan<T>(ReadOnlyUnsafeCollection<T> collection)
|
||||||
|
{
|
||||||
|
return collection.AsSpan();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ public unsafe struct UnTypedArray : IUnTypedCollection
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryHandle memHandle = _memoryHandle;
|
var memHandle = _memoryHandle;
|
||||||
_buffer = _allocationHandle.Realloc(_allocationHandle.State, _buffer, _size, newSize, _alignment, option, &memHandle);
|
_buffer = _allocationHandle.Realloc(_allocationHandle.State, _buffer, _size, newSize, _alignment, option, &memHandle);
|
||||||
_size = newSize;
|
_size = newSize;
|
||||||
_memoryHandle = memHandle;
|
_memoryHandle = memHandle;
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ using Misaki.HighPerformance.LowLevel.Collections.Contracts;
|
|||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Drawing;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|
||||||
@@ -26,7 +24,7 @@ internal class UnsafeArrayDebugView<T>
|
|||||||
{
|
{
|
||||||
var count = _array.Count;
|
var count = _array.Count;
|
||||||
var result = new T[count];
|
var result = new T[count];
|
||||||
for (int i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
result[i] = _array[i];
|
result[i] = _array[i];
|
||||||
}
|
}
|
||||||
@@ -78,7 +76,9 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
|
|
||||||
private T* _buffer;
|
private T* _buffer;
|
||||||
private int _count;
|
private int _count;
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
private MemoryHandle _memoryHandle;
|
private MemoryHandle _memoryHandle;
|
||||||
|
#endif
|
||||||
private AllocationHandle _allocationHandle;
|
private AllocationHandle _allocationHandle;
|
||||||
|
|
||||||
public readonly int Count => _count;
|
public readonly int Count => _count;
|
||||||
@@ -108,6 +108,7 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
if (_buffer != null)
|
if (_buffer != null)
|
||||||
{
|
{
|
||||||
if (_allocationHandle.IsValid != null)
|
if (_allocationHandle.IsValid != null)
|
||||||
@@ -121,6 +122,9 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
#else
|
||||||
|
return _buffer != null;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,11 +167,19 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
throw new InvalidOperationException("Target allocation handle does not support allocation.");
|
throw new InvalidOperationException("Target allocation handle does not support allocation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
MemoryHandle memHandle;
|
MemoryHandle memHandle;
|
||||||
var buff = handle.Alloc(handle.State, (nuint)(count * sizeof(T)), AlignOf<T>(), allocationOption, &memHandle);
|
#endif
|
||||||
|
var buff = handle.Alloc(handle.State, (nuint)(count * sizeof(T)), AlignOf<T>(), allocationOption
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
|
, &memHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
_buffer = (T*)buff;
|
_buffer = (T*)buff;
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
_memoryHandle = memHandle;
|
_memoryHandle = memHandle;
|
||||||
|
#endif
|
||||||
_allocationHandle = handle;
|
_allocationHandle = handle;
|
||||||
_count = count;
|
_count = count;
|
||||||
}
|
}
|
||||||
@@ -246,10 +258,18 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
MemoryHandle memHandle = _memoryHandle;
|
MemoryHandle memHandle = _memoryHandle;
|
||||||
|
#endif
|
||||||
var elemSize = SizeOf<T>();
|
var elemSize = SizeOf<T>();
|
||||||
_buffer = (T*)_allocationHandle.Realloc(_allocationHandle.State, _buffer, (nuint)Count * elemSize, (nuint)newSize * elemSize, AlignOf<T>(), option, &memHandle);
|
_buffer = (T*)_allocationHandle.Realloc(_allocationHandle.State, _buffer, (nuint)Count * elemSize, (nuint)newSize * elemSize, AlignOf<T>(), option
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
|
, &memHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
_memoryHandle = memHandle;
|
_memoryHandle = memHandle;
|
||||||
|
#endif
|
||||||
_count = newSize;
|
_count = newSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,7 +423,11 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
|
|||||||
|
|
||||||
if (_allocationHandle.Free != null)
|
if (_allocationHandle.Free != null)
|
||||||
{
|
{
|
||||||
_allocationHandle.Free(_allocationHandle.State, _buffer, _memoryHandle);
|
_allocationHandle.Free(_allocationHandle.State, _buffer
|
||||||
|
#if ENABLE_DEBUG_LAYER
|
||||||
|
, _memoryHandle
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer = null;
|
_buffer = null;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -40,7 +39,7 @@ public unsafe struct UnsafeBitSet : IDisposable, IEquatable<UnsafeBitSet>
|
|||||||
private UnsafeBitSet _bitSet;
|
private UnsafeBitSet _bitSet;
|
||||||
private int _currentBit;
|
private int _currentBit;
|
||||||
|
|
||||||
public Iterator (UnsafeBitSet bitSet)
|
public Iterator(UnsafeBitSet bitSet)
|
||||||
{
|
{
|
||||||
_bitSet = bitSet;
|
_bitSet = bitSet;
|
||||||
_currentBit = -1;
|
_currentBit = -1;
|
||||||
@@ -116,7 +115,7 @@ public unsafe struct UnsafeBitSet : IDisposable, IEquatable<UnsafeBitSet>
|
|||||||
public UnsafeBitSet(Span<uint> bits, Allocator allocator)
|
public UnsafeBitSet(Span<uint> bits, Allocator allocator)
|
||||||
{
|
{
|
||||||
_bits = new UnsafeArray<uint>(bits.Length, allocator, AllocationOption.None);
|
_bits = new UnsafeArray<uint>(bits.Length, allocator, AllocationOption.None);
|
||||||
_bits.CopyFrom<UnsafeArray<uint>, uint>(bits);
|
_bits.CopyFrom(bits);
|
||||||
|
|
||||||
_highestBit = 0;
|
_highestBit = 0;
|
||||||
_max = _bits.Count * (_BIT_SIZE + 1) - 1; // Calculate the maximum index in use
|
_max = _bits.Count * (_BIT_SIZE + 1) - 1; // Calculate the maximum index in use
|
||||||
@@ -135,9 +134,9 @@ public unsafe struct UnsafeBitSet : IDisposable, IEquatable<UnsafeBitSet>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines the required length of an <see cref="UnsafeBitSet"/> to hold the passed id or bit.
|
/// Determines the required length of an <see cref="UnsafeBitSet"/> to hold the passed ID or bit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The id or bit.</param>
|
/// <param name="id">The ID or bit.</param>
|
||||||
/// <returns>A size of required <see cref="uint"/>s for the bitset.</returns>
|
/// <returns>A size of required <see cref="uint"/>s for the bitset.</returns>
|
||||||
public static int RequiredLength(int id)
|
public static int RequiredLength(int id)
|
||||||
{
|
{
|
||||||
@@ -788,14 +787,14 @@ public unsafe struct UnsafeBitSet : IDisposable, IEquatable<UnsafeBitSet>
|
|||||||
return !(left == right);
|
return !(left == right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly override int GetHashCode()
|
public override readonly int GetHashCode()
|
||||||
{
|
{
|
||||||
var hash = new HashCode();
|
var hash = new HashCode();
|
||||||
hash.AddBytes(MemoryMarshal.AsBytes(_bits.AsSpan()));
|
hash.AddBytes(MemoryMarshal.AsBytes(_bits.AsSpan()));
|
||||||
return hash.ToHashCode();
|
return hash.ToHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly override string ToString()
|
public override readonly string ToString()
|
||||||
{
|
{
|
||||||
// Convert uint to binary form for pretty printing
|
// Convert uint to binary form for pretty printing
|
||||||
var binaryBuilder = new StringBuilder();
|
var binaryBuilder = new StringBuilder();
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Misaki.HighPerformance.LowLevel.Buffer;
|
|||||||
using Misaki.HighPerformance.LowLevel.Collections.Contracts;
|
using Misaki.HighPerformance.LowLevel.Collections.Contracts;
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Collections;
|
namespace Misaki.HighPerformance.LowLevel.Collections;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public unsafe struct UnsafeHashSet<T> : IUnsafeHashCollection<T>, IEnumerable<T>
|
|||||||
{
|
{
|
||||||
internal HashMapHelper<T>.Enumerator _enumerator;
|
internal HashMapHelper<T>.Enumerator _enumerator;
|
||||||
|
|
||||||
public readonly T Current => _enumerator.buffer->_keys[_enumerator.index];
|
public readonly T Current => _enumerator.buffer->_keys[_enumerator.index];
|
||||||
readonly object IEnumerator.Current => Current;
|
readonly object IEnumerator.Current => Current;
|
||||||
|
|
||||||
public Enumerator(HashMapHelper<T>* hashMap)
|
public Enumerator(HashMapHelper<T>* hashMap)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ internal class UnsafeSlotMapDebugView<T>
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides an unsafe, high-performance slot map for storing and managing unmanaged values, supporting fast insertion,
|
/// Provides an unsafe, high-performance slot map for storing and managing unmanaged values, supporting fast insertion,
|
||||||
/// removal, and lookup by slot index and generation.
|
/// removal, and lookup by slot index and Generation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of value to store in the slot map. Must be unmanaged.</typeparam>
|
/// <typeparam name="T">The type of value to store in the slot map. Must be unmanaged.</typeparam>
|
||||||
[DebuggerTypeProxy(typeof(UnsafeSlotMapDebugView<>))]
|
[DebuggerTypeProxy(typeof(UnsafeSlotMapDebugView<>))]
|
||||||
@@ -155,7 +155,7 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
/// Adds the specified item to the collection and returns the index of the slot where it was stored.
|
/// Adds the specified item to the collection and returns the index of the slot where it was stored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item to add to the collection.</param>
|
/// <param name="item">The item to add to the collection.</param>
|
||||||
/// <param name="generation">When this method returns, contains the generation number associated with the slot where the item was stored.</param>
|
/// <param name="generation">When this method returns, contains the Generation number associated with the slot where the item was stored.</param>
|
||||||
/// <returns>The index of the slot in which the item was stored.</returns>
|
/// <returns>The index of the slot in which the item was stored.</returns>
|
||||||
public int Add(T item, out int generation)
|
public int Add(T item, out int generation)
|
||||||
{
|
{
|
||||||
@@ -184,10 +184,10 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to remove the item at the specified slot index and generation from the collection.
|
/// Attempts to remove the item at the specified slot index and Generation from the collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slotIndex">The zero-based index of the slot to remove. Must be within the valid range of slot indices.</param>
|
/// <param name="slotIndex">The zero-based index of the slot to remove. Must be within the valid range of slot indices.</param>
|
||||||
/// <param name="generation">The generation value associated with the slot. Removal succeeds only if this matches the current generation of the slot.</param>
|
/// <param name="generation">The Generation value associated with the slot. Removal succeeds only if this matches the current Generation of the slot.</param>
|
||||||
/// <param name="item">When this method returns, contains the item that was removed if the removal was successful; otherwise, the default value for type <typeparamref name="T"/>.</param>
|
/// <param name="item">When this method returns, contains the item that was removed if the removal was successful; otherwise, the default value for type <typeparamref name="T"/>.</param>
|
||||||
/// <returns>true if the item was successfully removed; otherwise, false.</returns>
|
/// <returns>true if the item was successfully removed; otherwise, false.</returns>
|
||||||
public bool Remove(int slotIndex, int generation, out T item)
|
public bool Remove(int slotIndex, int generation, out T item)
|
||||||
@@ -216,10 +216,10 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to remove the item at the specified slot index and generation from the collection.
|
/// Attempts to remove the item at the specified slot index and Generation from the collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slotIndex">The zero-based index of the slot to remove. Must be within the valid range of slot indices.</param>
|
/// <param name="slotIndex">The zero-based index of the slot to remove. Must be within the valid range of slot indices.</param>
|
||||||
/// <param name="generation">The generation value associated with the slot. Removal succeeds only if this matches the current generation of
|
/// <param name="generation">The Generation value associated with the slot. Removal succeeds only if this matches the current Generation of
|
||||||
/// the slot.</param>
|
/// the slot.</param>
|
||||||
/// <returns>true if the item was successfully removed; otherwise, false.</returns>
|
/// <returns>true if the item was successfully removed; otherwise, false.</returns>
|
||||||
public bool Remove(int slotIndex, int generation)
|
public bool Remove(int slotIndex, int generation)
|
||||||
@@ -228,11 +228,11 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified slot index contains a valid entry with the given generation.
|
/// Determines whether the specified slot index contains a valid entry with the given Generation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slotIndex">The zero-based index of the slot to check. Must be greater than or equal to 0 and less than the current capacity.</param>
|
/// <param name="slotIndex">The zero-based index of the slot to check. Must be greater than or equal to 0 and less than the current capacity.</param>
|
||||||
/// <param name="generation">The generation value to compare against the slot's generation.</param>
|
/// <param name="generation">The Generation value to compare against the slot's Generation.</param>
|
||||||
/// <returns>true if the slot at the specified index is valid and its generation matches the specified value; otherwise, false.</returns>
|
/// <returns>true if the slot at the specified index is valid and its Generation matches the specified value; otherwise, false.</returns>
|
||||||
public readonly bool Contains(int slotIndex, int generation)
|
public readonly bool Contains(int slotIndex, int generation)
|
||||||
{
|
{
|
||||||
if (slotIndex < 0 || slotIndex >= _capacity)
|
if (slotIndex < 0 || slotIndex >= _capacity)
|
||||||
@@ -249,14 +249,14 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to retrieve the element at the specified slot index and generation.
|
/// Attempts to retrieve the element at the specified slot index and Generation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slotIndex">The zero-based index of the slot to retrieve. Must be within the valid range of slots.</param>
|
/// <param name="slotIndex">The zero-based index of the slot to retrieve. Must be within the valid range of slots.</param>
|
||||||
/// <param name="generation">The generation identifier associated with the slot. Used to verify that the slot has not been replaced or
|
/// <param name="generation">The Generation identifier associated with the slot. Used to verify that the slot has not been replaced or
|
||||||
/// invalidated.</param>
|
/// invalidated.</param>
|
||||||
/// <param name="value">When this method returns, contains the element at the specified slot and generation if found; otherwise, the
|
/// <param name="value">When this method returns, contains the element at the specified slot and Generation if found; otherwise, the
|
||||||
/// default value for type <typeparamref name="T"/>.</param>
|
/// default value for type <typeparamref name="T"/>.</param>
|
||||||
/// <returns>true if the element at the specified slot index and generation is found; otherwise, false.</returns>
|
/// <returns>true if the element at the specified slot index and Generation is found; otherwise, false.</returns>
|
||||||
public readonly bool TryGetElementAt(int slotIndex, int generation, out T value)
|
public readonly bool TryGetElementAt(int slotIndex, int generation, out T value)
|
||||||
{
|
{
|
||||||
if (!Contains(slotIndex, generation))
|
if (!Contains(slotIndex, generation))
|
||||||
@@ -270,13 +270,13 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the element stored at the specified slot index and generation.
|
/// Retrieves the element stored at the specified slot index and Generation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slotIndex">The zero-based index of the slot from which to retrieve the element. Must be within the valid range of allocated slots.</param>
|
/// <param name="slotIndex">The zero-based index of the slot from which to retrieve the element. Must be within the valid range of allocated slots.</param>
|
||||||
/// <param name="generation">The generation identifier associated with the slot. Used to ensure the element has not been replaced or removed since allocation.</param>
|
/// <param name="generation">The Generation identifier associated with the slot. Used to ensure the element has not been replaced or removed since allocation.</param>
|
||||||
/// <returns>The element of type <see cref="T"/> stored at the specified slot and generation.</returns>
|
/// <returns>The element of type <see cref="T"/> stored at the specified slot and Generation.</returns>
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="slotIndex"/> is less than zero or greater than or equal to the capacity.</exception>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="slotIndex"/> is less than zero or greater than or equal to the capacity.</exception>
|
||||||
/// <exception cref="InvalidOperationException">Thrown when the specified slot is not occupied or the generation does not match.</exception>
|
/// <exception cref="InvalidOperationException">Thrown when the specified slot is not occupied or the Generation does not match.</exception>
|
||||||
public readonly T GetElementAt(int slotIndex, int generation)
|
public readonly T GetElementAt(int slotIndex, int generation)
|
||||||
{
|
{
|
||||||
if (!Contains(slotIndex, generation))
|
if (!Contains(slotIndex, generation))
|
||||||
@@ -288,13 +288,13 @@ public unsafe struct UnsafeSlotMap<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a reference to the element at the specified slot index and generation, if it exists; otherwise, returns
|
/// Returns a reference to the element at the specified slot index and Generation, if it exists; otherwise, returns
|
||||||
/// a null reference.
|
/// a null reference.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="slotIndex">The zero-based index of the slot to retrieve. Must be within the valid range of allocated slots.</param>
|
/// <param name="slotIndex">The zero-based index of the slot to retrieve. Must be within the valid range of allocated slots.</param>
|
||||||
/// <param name="generation">The expected generation value for the slot. Used to verify that the slot has not been recycled or replaced.</param>
|
/// <param name="generation">The expected Generation value for the slot. Used to verify that the slot has not been recycled or replaced.</param>
|
||||||
/// <param name="exist">When this method returns, contains <see langword="true"/> if a valid element exists at the specified slot and generation; otherwise, <see langword="false"/>.</param>
|
/// <param name="exist">When this method returns, contains <see langword="true"/> if a valid element exists at the specified slot and Generation; otherwise, <see langword="false"/>.</param>
|
||||||
/// <returns>A reference to the element of type <typeparamref name="T"/> at the specified slot and generation if it exists; otherwise, a null reference.</returns>
|
/// <returns>A reference to the element of type <typeparamref name="T"/> at the specified slot and Generation if it exists; otherwise, a null reference.</returns>
|
||||||
public ref T GetElementReferenceAt(int slotIndex, int generation, out bool exist)
|
public ref T GetElementReferenceAt(int slotIndex, int generation, out bool exist)
|
||||||
{
|
{
|
||||||
if (!Contains(slotIndex, generation))
|
if (!Contains(slotIndex, generation))
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
private UnsafeArray<T> _dense;
|
private UnsafeArray<T> _dense;
|
||||||
private UnsafeArray<int> _generations;
|
private UnsafeArray<int> _generations;
|
||||||
private UnsafeArray<int> _sparse;
|
private UnsafeArray<int> _sparse;
|
||||||
private UnsafeArray<int> _reverse; // Maps dense index to sparse index. Since this is a general purpose sparse set, we have to include reverse array. In real world ecs, this should be replaced with entity id array.
|
private UnsafeArray<int> _reverse; // Maps dense index to sparse index. Since this is a general purpose sparse set, we have to include reverse array. In real world ecs, this should be replaced with entity ID array.
|
||||||
private UnsafeStack<int> _freeSparse;
|
private UnsafeStack<int> _freeSparse;
|
||||||
|
|
||||||
private int _count;
|
private int _count;
|
||||||
@@ -168,7 +168,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
/// Adds a value to the sparse set and returns a unique sparse index for the value.
|
/// Adds a value to the sparse set and returns a unique sparse index for the value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The value to add to the sparse set.</param>
|
/// <param name="value">The value to add to the sparse set.</param>
|
||||||
/// <param name="generation">Outputs the generation number associated with the added value.</param>
|
/// <param name="generation">Outputs the Generation number associated with the added value.</param>
|
||||||
/// <returns>A unique sparse index that can be used to reference this value.</returns>
|
/// <returns>A unique sparse index that can be used to reference this value.</returns>
|
||||||
public int Add(T value, out int generation)
|
public int Add(T value, out int generation)
|
||||||
{
|
{
|
||||||
@@ -205,7 +205,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
/// Removes the value at the specified sparse index.
|
/// Removes the value at the specified sparse index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sparseIndex">The sparse index of the value to remove.</param>
|
/// <param name="sparseIndex">The sparse index of the value to remove.</param>
|
||||||
/// <param name="generation">The generation number associated with the sparse index to validate.</param>
|
/// <param name="generation">The Generation number associated with the sparse index to validate.</param>
|
||||||
/// <param name="item">When this method returns, contains the item that was removed if the removal was successful; otherwise, the default value for type <typeparamref name="T"/>.</param>
|
/// <param name="item">When this method returns, contains the item that was removed if the removal was successful; otherwise, the default value for type <typeparamref name="T"/>.</param>
|
||||||
/// <returns>True if the value was removed, false if the sparse index was not found.</returns>
|
/// <returns>True if the value was removed, false if the sparse index was not found.</returns>
|
||||||
public bool Remove(int sparseIndex, int generation, out T item)
|
public bool Remove(int sparseIndex, int generation, out T item)
|
||||||
@@ -234,7 +234,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
|
|
||||||
// Mark the sparse index as unused and add to free list
|
// Mark the sparse index as unused and add to free list
|
||||||
_sparse[sparseIndex] = -1;
|
_sparse[sparseIndex] = -1;
|
||||||
_generations[sparseIndex]++; // Increment generation to invalidate old references
|
_generations[sparseIndex]++; // Increment Generation to invalidate old references
|
||||||
|
|
||||||
item = _dense[denseIndex];
|
item = _dense[denseIndex];
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
/// Removes the value at the specified sparse index.
|
/// Removes the value at the specified sparse index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sparseIndex">The sparse index of the value to remove.</param>
|
/// <param name="sparseIndex">The sparse index of the value to remove.</param>
|
||||||
/// <param name="generation">The generation number associated with the sparse index to validate.</param>
|
/// <param name="generation">The Generation number associated with the sparse index to validate.</param>
|
||||||
/// <returns>True if the value was removed, false if the sparse index was not found.</returns>
|
/// <returns>True if the value was removed, false if the sparse index was not found.</returns>
|
||||||
public bool Remove(int sparseIndex, int generation)
|
public bool Remove(int sparseIndex, int generation)
|
||||||
{
|
{
|
||||||
@@ -275,7 +275,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
|
|
||||||
// Mark the sparse index as unused and add to free list
|
// Mark the sparse index as unused and add to free list
|
||||||
_sparse[sparseIndex] = -1;
|
_sparse[sparseIndex] = -1;
|
||||||
_generations[sparseIndex]++; // Increment generation to invalidate old references
|
_generations[sparseIndex]++; // Increment Generation to invalidate old references
|
||||||
|
|
||||||
_freeSparse.Push(sparseIndex);
|
_freeSparse.Push(sparseIndex);
|
||||||
_count--;
|
_count--;
|
||||||
@@ -287,7 +287,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
/// Checks if the sparse set contains a value at the specified sparse index.
|
/// Checks if the sparse set contains a value at the specified sparse index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sparseIndex">The sparse index to check.</param>
|
/// <param name="sparseIndex">The sparse index to check.</param>
|
||||||
/// <param name="generation">The generation number to validate against the stored generation.</param>
|
/// <param name="generation">The Generation number to validate against the stored Generation.</param>
|
||||||
/// <returns>True if the sparse index is valid and contains a value, false otherwise.</returns>
|
/// <returns>True if the sparse index is valid and contains a value, false otherwise.</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly bool Contains(int sparseIndex, int generation)
|
public readonly bool Contains(int sparseIndex, int generation)
|
||||||
@@ -302,10 +302,10 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the value at the specified sparse index and generation.
|
/// Gets the value at the specified sparse index and Generation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sparseIndex">The sparse index to retrieve the value from.</param>
|
/// <param name="sparseIndex">The sparse index to retrieve the value from.</param>
|
||||||
/// <param name="generation">The generation number to validate against the stored generation.</param>
|
/// <param name="generation">The Generation number to validate against the stored Generation.</param>
|
||||||
/// <param name="value">When this method returns, contains the value at the specified sparse index, if found.</param>
|
/// <param name="value">When this method returns, contains the value at the specified sparse index, if found.</param>
|
||||||
/// <returns>True if the sparse index contains a value, false otherwise.</returns>
|
/// <returns>True if the sparse index contains a value, false otherwise.</returns>
|
||||||
public readonly bool TryGetValue(int sparseIndex, int generation, out T value)
|
public readonly bool TryGetValue(int sparseIndex, int generation, out T value)
|
||||||
@@ -321,10 +321,10 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the value at the specified sparse index and generation.
|
/// Gets the value at the specified sparse index and Generation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sparseIndex">The sparse index to retrieve the value from.</param>
|
/// <param name="sparseIndex">The sparse index to retrieve the value from.</param>
|
||||||
/// <param name="generation">The generation number to validate against the stored generation.</param>
|
/// <param name="generation">The Generation number to validate against the stored Generation.</param>
|
||||||
/// <returns>The value at the specified sparse index.</returns>
|
/// <returns>The value at the specified sparse index.</returns>
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the sparse index is not found.</exception>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when the sparse index is not found.</exception>
|
||||||
public readonly T GetValue(int sparseIndex, int generation)
|
public readonly T GetValue(int sparseIndex, int generation)
|
||||||
@@ -338,10 +338,10 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets reference of the value at the specified sparse index and generation.
|
/// Gets reference of the value at the specified sparse index and Generation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sparseIndex">The sparse index to retrieve the value from.</param>
|
/// <param name="sparseIndex">The sparse index to retrieve the value from.</param>
|
||||||
/// <param name="generation">The generation number to validate against the stored generation.</param>
|
/// <param name="generation">The Generation number to validate against the stored Generation.</param>
|
||||||
/// <param name="exist">Outputs whether the sparse index exists in the set.</param>
|
/// <param name="exist">Outputs whether the sparse index exists in the set.</param>
|
||||||
/// <returns>Reference of the value at the specified sparse index.</returns>
|
/// <returns>Reference of the value at the specified sparse index.</returns>
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the sparse index is not found.</exception>
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when the sparse index is not found.</exception>
|
||||||
@@ -361,7 +361,7 @@ public unsafe struct UnsafeSparseSet<T> : IUnsafeCollection<T>
|
|||||||
/// Updates the value at the specified sparse index.
|
/// Updates the value at the specified sparse index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sparseIndex">The sparse index of the value to update.</param>
|
/// <param name="sparseIndex">The sparse index of the value to update.</param>
|
||||||
/// <param name="generation">The generation number to validate against the stored generation.</param>
|
/// <param name="generation">The Generation number to validate against the stored Generation.</param>
|
||||||
/// <param name="value">The new value.</param>
|
/// <param name="value">The new value.</param>
|
||||||
/// <returns>True if the value was updated, false if the sparse index was not found.</returns>
|
/// <returns>True if the value was updated, false if the sparse index was not found.</returns>
|
||||||
public bool SetValue(int sparseIndex, int generation, T value)
|
public bool SetValue(int sparseIndex, int generation, T value)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ internal class UnsafeStackDebugView<T>
|
|||||||
{
|
{
|
||||||
var items = new T[_stack.Count];
|
var items = new T[_stack.Count];
|
||||||
var pItems = (T*)_stack.GetUnsafePtr();
|
var pItems = (T*)_stack.GetUnsafePtr();
|
||||||
for (int i = 0; i < _stack.Count; i++)
|
for (var i = 0; i < _stack.Count; i++)
|
||||||
{
|
{
|
||||||
items[i] = pItems[i];
|
items[i] = pItems[i];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel;
|
namespace Misaki.HighPerformance.LowLevel;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<IsAotCompatible>True</IsAotCompatible>
|
<IsAotCompatible>True</IsAotCompatible>
|
||||||
|
<DefineConstants>$(DefineConstants);ENABLE_SAFETY_CHECKS</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
@@ -36,6 +37,10 @@
|
|||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="TerraFX.Interop.Mimalloc" Version="1.6.7.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="Collections\FixedString.tt">
|
<None Update="Collections\FixedString.tt">
|
||||||
<Generator>TextTemplatingFileGenerator</Generator>
|
<Generator>TextTemplatingFileGenerator</Generator>
|
||||||
|
|||||||
@@ -41,10 +41,31 @@ This package is the lowest-level layer in the solution. It is intended for code
|
|||||||
## Example
|
## Example
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// The low-level layer is meant for advanced ownership and allocation scenarios.
|
var opts = new AllocationManagerInitOpts
|
||||||
// Prefer the higher-level packages when they already satisfy your use case.
|
{
|
||||||
|
ArenaCapacity = 1024 * 1024,
|
||||||
|
StackCapacity = 1024 * 1024,
|
||||||
|
FreeListConcurrencyLevel = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
AllocationManager.Initialize(opts);
|
||||||
|
|
||||||
|
var arr = new UnsafeArray<int>(10, Allocator.Persistent);
|
||||||
|
|
||||||
|
// Use the array
|
||||||
|
|
||||||
|
arr.Dispose();
|
||||||
|
|
||||||
|
AllocationManager.Dispose();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can enable debug features for leak detection and use-after-free checks by defining `ENABLE_SAFETY_CHECKS` in your project. And define `ENABLE_DEBUG_LAYER` to enable additional debug features such as tracking allocations and providing detailed error messages.
|
||||||
|
> Which means if you disable the safety checks, the library will not perform any safety checks and provide the maximum performance, and it will be your responsibility to ensure correct usage to avoid memory leaks and undefined behavior.
|
||||||
|
> Even `IUnsafeCollection.IsCreated` will only check if the internal pointer is non-null, without verifying the actual validity of the memory.
|
||||||
|
|
||||||
|
You can also define `ENABLE_MIMALLOC` to use mimalloc as the underlying allocator instead of the default C allocator.
|
||||||
|
> Using mimalloc requires to install the TerraFX.Interop.Mimalloc package and ensure the native mimalloc library is available at runtime.
|
||||||
|
|
||||||
## Package reference
|
## Package reference
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
#if ENABLE_MIMALLOC
|
||||||
|
using TerraFX.Interop.Mimalloc;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Utilities;
|
namespace Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
|
||||||
@@ -66,7 +69,9 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void* Malloc(nuint size)
|
public static void* Malloc(nuint size)
|
||||||
{
|
{
|
||||||
#if NET6_0_OR_GREATER
|
#if ENABLE_MIMALLOC
|
||||||
|
return Mimalloc.mi_malloc(size);
|
||||||
|
#elif NET6_0_OR_GREATER
|
||||||
return NativeMemory.Alloc(size);
|
return NativeMemory.Alloc(size);
|
||||||
#else
|
#else
|
||||||
return Marshal.AllocHGlobal((IntPtr)size).ToPointer();
|
return Marshal.AllocHGlobal((IntPtr)size).ToPointer();
|
||||||
@@ -81,7 +86,9 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void* Calloc(nuint size)
|
public static void* Calloc(nuint size)
|
||||||
{
|
{
|
||||||
#if NET6_0_OR_GREATER
|
#if ENABLE_MIMALLOC
|
||||||
|
return Mimalloc.mi_zalloc(size);
|
||||||
|
#elif NET6_0_OR_GREATER
|
||||||
return NativeMemory.AllocZeroed(size);
|
return NativeMemory.AllocZeroed(size);
|
||||||
#else
|
#else
|
||||||
var ptr = Marshal.AllocHGlobal((IntPtr)size).ToPointer();
|
var ptr = Marshal.AllocHGlobal((IntPtr)size).ToPointer();
|
||||||
@@ -99,7 +106,9 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void* AlignedAlloc(nuint size, nuint alignment)
|
public static void* AlignedAlloc(nuint size, nuint alignment)
|
||||||
{
|
{
|
||||||
#if NET6_0_OR_GREATER
|
#if ENABLE_MIMALLOC
|
||||||
|
return Mimalloc.mi_aligned_alloc(alignment, size);
|
||||||
|
#elif NET6_0_OR_GREATER
|
||||||
return NativeMemory.AlignedAlloc(size, alignment);
|
return NativeMemory.AlignedAlloc(size, alignment);
|
||||||
#else
|
#else
|
||||||
return Marshal.AllocHGlobal((IntPtr)(size + alignment - 1)).ToPointer();
|
return Marshal.AllocHGlobal((IntPtr)(size + alignment - 1)).ToPointer();
|
||||||
@@ -115,7 +124,9 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void* Realloc(void* ptr, nuint size)
|
public static void* Realloc(void* ptr, nuint size)
|
||||||
{
|
{
|
||||||
#if NET6_0_OR_GREATER
|
#if ENABLE_MIMALLOC
|
||||||
|
return Mimalloc.mi_realloc(ptr, size);
|
||||||
|
#elif NET6_0_OR_GREATER
|
||||||
return NativeMemory.Realloc(ptr, size);
|
return NativeMemory.Realloc(ptr, size);
|
||||||
#else
|
#else
|
||||||
return Marshal.ReAllocHGlobal((IntPtr)ptr, (IntPtr)size).ToPointer();
|
return Marshal.ReAllocHGlobal((IntPtr)ptr, (IntPtr)size).ToPointer();
|
||||||
@@ -133,7 +144,9 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void* AlignedRealloc(void* ptr, nuint size, nuint alignment)
|
public static void* AlignedRealloc(void* ptr, nuint size, nuint alignment)
|
||||||
{
|
{
|
||||||
#if NET6_0_OR_GREATER
|
#if ENABLE_MIMALLOC
|
||||||
|
return Mimalloc.mi_realloc_aligned(ptr, size, alignment);
|
||||||
|
#elif NET6_0_OR_GREATER
|
||||||
return NativeMemory.AlignedRealloc(ptr, size, alignment);
|
return NativeMemory.AlignedRealloc(ptr, size, alignment);
|
||||||
#else
|
#else
|
||||||
var newPtr = Marshal.AllocHGlobal((IntPtr)(size + alignment - 1)).ToPointer();
|
var newPtr = Marshal.AllocHGlobal((IntPtr)(size + alignment - 1)).ToPointer();
|
||||||
@@ -159,7 +172,9 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void Free(void* ptr)
|
public static void Free(void* ptr)
|
||||||
{
|
{
|
||||||
#if NET6_0_OR_GREATER
|
#if ENABLE_MIMALLOC
|
||||||
|
Mimalloc.mi_free(ptr);
|
||||||
|
#elif NET6_0_OR_GREATER
|
||||||
NativeMemory.Free(ptr);
|
NativeMemory.Free(ptr);
|
||||||
#else
|
#else
|
||||||
Marshal.FreeHGlobal((IntPtr)ptr);
|
Marshal.FreeHGlobal((IntPtr)ptr);
|
||||||
@@ -174,7 +189,9 @@ public static unsafe partial class MemoryUtility
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void AlignedFree(void* ptr)
|
public static void AlignedFree(void* ptr)
|
||||||
{
|
{
|
||||||
#if NET6_0_OR_GREATER
|
#if ENABLE_MIMALLOC
|
||||||
|
Mimalloc.mi_free(ptr);
|
||||||
|
#elif NET6_0_OR_GREATER
|
||||||
NativeMemory.AlignedFree(ptr);
|
NativeMemory.AlignedFree(ptr);
|
||||||
#else
|
#else
|
||||||
Marshal.FreeHGlobal((IntPtr)ptr);
|
Marshal.FreeHGlobal((IntPtr)ptr);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections.Contracts;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.LowLevel.Utilities;
|
namespace Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
|
|||||||
@@ -83,14 +83,14 @@ public unsafe struct Vector2<TLane, TNumber> : IEquatable<Vector2<TLane, TNumber
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Store(TNumber* px, TNumber* py)
|
public void Store(TNumber* px, TNumber* py)
|
||||||
{
|
{
|
||||||
x.Store(px);
|
x.Store(px);
|
||||||
y.Store(py);
|
y.Store(py);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Store(ref TNumber x, ref TNumber y)
|
public void Store(ref TNumber x, ref TNumber y)
|
||||||
{
|
{
|
||||||
this.x.Store(ref x);
|
this.x.Store(ref x);
|
||||||
this.y.Store(ref y);
|
this.y.Store(ref y);
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Store(TNumber* px, TNumber* py, TNumber* pz)
|
public void Store(TNumber* px, TNumber* py, TNumber* pz)
|
||||||
{
|
{
|
||||||
x.Store(px);
|
x.Store(px);
|
||||||
y.Store(py);
|
y.Store(py);
|
||||||
@@ -97,7 +97,7 @@ public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Store(ref TNumber x, ref TNumber y, ref TNumber z)
|
public void Store(ref TNumber x, ref TNumber y, ref TNumber z)
|
||||||
{
|
{
|
||||||
this.x.Store(ref x);
|
this.x.Store(ref x);
|
||||||
this.y.Store(ref y);
|
this.y.Store(ref y);
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Store(TNumber* px, TNumber* py, TNumber* pz, TNumber* pw)
|
public void Store(TNumber* px, TNumber* py, TNumber* pz, TNumber* pw)
|
||||||
{
|
{
|
||||||
x.Store(px);
|
x.Store(px);
|
||||||
y.Store(py);
|
y.Store(py);
|
||||||
@@ -104,7 +104,7 @@ public unsafe struct Vector4<TLane, TNumber> : IEquatable<Vector4<TLane, TNumber
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Store(ref TNumber x, ref TNumber y, ref TNumber z, ref TNumber w)
|
public void Store(ref TNumber x, ref TNumber y, ref TNumber z, ref TNumber w)
|
||||||
{
|
{
|
||||||
this.x.Store(ref x);
|
this.x.Store(ref x);
|
||||||
this.y.Store(ref y);
|
this.y.Store(ref y);
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Mathematics;
|
namespace Misaki.HighPerformance.Mathematics;
|
||||||
|
|
||||||
internal class AutoSIMDAttribute
|
internal class AutoSIMDAttribute
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2049,8 +2049,8 @@ public static partial class math
|
|||||||
public static int2 clamp(int2 valueToClamp, int2 lowerBound, int2 upperBound)
|
public static int2 clamp(int2 valueToClamp, int2 lowerBound, int2 upperBound)
|
||||||
{
|
{
|
||||||
return new int2(
|
return new int2(
|
||||||
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
||||||
clamp(valueToClamp.y, lowerBound.y, upperBound.y)
|
clamp(valueToClamp.y, lowerBound.y, upperBound.y)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2063,9 +2063,9 @@ public static partial class math
|
|||||||
public static int3 clamp(int3 valueToClamp, int3 lowerBound, int3 upperBound)
|
public static int3 clamp(int3 valueToClamp, int3 lowerBound, int3 upperBound)
|
||||||
{
|
{
|
||||||
return new int3(
|
return new int3(
|
||||||
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
||||||
clamp(valueToClamp.y, lowerBound.y, upperBound.y),
|
clamp(valueToClamp.y, lowerBound.y, upperBound.y),
|
||||||
clamp(valueToClamp.z, lowerBound.z, upperBound.z)
|
clamp(valueToClamp.z, lowerBound.z, upperBound.z)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2101,8 +2101,8 @@ public static partial class math
|
|||||||
public static uint2 clamp(uint2 valueToClamp, uint2 lowerBound, uint2 upperBound)
|
public static uint2 clamp(uint2 valueToClamp, uint2 lowerBound, uint2 upperBound)
|
||||||
{
|
{
|
||||||
return new uint2(
|
return new uint2(
|
||||||
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
||||||
clamp(valueToClamp.y, lowerBound.y, upperBound.y));
|
clamp(valueToClamp.y, lowerBound.y, upperBound.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Returns the result of a componentwise clamping of the value valueToClamp into the interval (inclusive) [lowerBound, upperBound], where valueToClamp, lowerBound and upperBound are uint3 vectors.</summary>
|
/// <summary>Returns the result of a componentwise clamping of the value valueToClamp into the interval (inclusive) [lowerBound, upperBound], where valueToClamp, lowerBound and upperBound are uint3 vectors.</summary>
|
||||||
@@ -2114,9 +2114,9 @@ public static partial class math
|
|||||||
public static uint3 clamp(uint3 valueToClamp, uint3 lowerBound, uint3 upperBound)
|
public static uint3 clamp(uint3 valueToClamp, uint3 lowerBound, uint3 upperBound)
|
||||||
{
|
{
|
||||||
return new uint3(
|
return new uint3(
|
||||||
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
||||||
clamp(valueToClamp.y, lowerBound.y, upperBound.y),
|
clamp(valueToClamp.y, lowerBound.y, upperBound.y),
|
||||||
clamp(valueToClamp.z, lowerBound.z, upperBound.z)
|
clamp(valueToClamp.z, lowerBound.z, upperBound.z)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2175,8 +2175,8 @@ public static partial class math
|
|||||||
public static float2 clamp(float2 valueToClamp, float2 lowerBound, float2 upperBound)
|
public static float2 clamp(float2 valueToClamp, float2 lowerBound, float2 upperBound)
|
||||||
{
|
{
|
||||||
return new float2(
|
return new float2(
|
||||||
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
||||||
clamp(valueToClamp.y, lowerBound.y, upperBound.y)
|
clamp(valueToClamp.y, lowerBound.y, upperBound.y)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2189,9 +2189,9 @@ public static partial class math
|
|||||||
public static float3 clamp(float3 valueToClamp, float3 lowerBound, float3 upperBound)
|
public static float3 clamp(float3 valueToClamp, float3 lowerBound, float3 upperBound)
|
||||||
{
|
{
|
||||||
return new float3(
|
return new float3(
|
||||||
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
||||||
clamp(valueToClamp.y, lowerBound.y, upperBound.y),
|
clamp(valueToClamp.y, lowerBound.y, upperBound.y),
|
||||||
clamp(valueToClamp.z, lowerBound.z, upperBound.z)
|
clamp(valueToClamp.z, lowerBound.z, upperBound.z)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2238,9 +2238,9 @@ public static partial class math
|
|||||||
public static double3 clamp(double3 valueToClamp, double3 lowerBound, double3 upperBound)
|
public static double3 clamp(double3 valueToClamp, double3 lowerBound, double3 upperBound)
|
||||||
{
|
{
|
||||||
return new double3(
|
return new double3(
|
||||||
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
clamp(valueToClamp.x, lowerBound.x, upperBound.x),
|
||||||
clamp(valueToClamp.y, lowerBound.y, upperBound.y),
|
clamp(valueToClamp.y, lowerBound.y, upperBound.y),
|
||||||
clamp(valueToClamp.z, lowerBound.z, upperBound.z)
|
clamp(valueToClamp.z, lowerBound.z, upperBound.z)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8606,7 +8606,7 @@ public static partial class math
|
|||||||
var hash = seed + Prime5;
|
var hash = seed + Prime5;
|
||||||
if (numBytes >= 16)
|
if (numBytes >= 16)
|
||||||
{
|
{
|
||||||
uint4 state = new uint4(Prime1 + Prime2, Prime2, 0, (uint)-Prime1) + seed;
|
var state = new uint4(Prime1 + Prime2, Prime2, 0, (uint)-Prime1) + seed;
|
||||||
|
|
||||||
var count = numBytes >> 4;
|
var count = numBytes >> 4;
|
||||||
for (var i = 0; i < count; ++i)
|
for (var i = 0; i < count; ++i)
|
||||||
@@ -8659,7 +8659,7 @@ public static partial class math
|
|||||||
var hash = seed + Prime5;
|
var hash = seed + Prime5;
|
||||||
if (numBytes >= 16)
|
if (numBytes >= 16)
|
||||||
{
|
{
|
||||||
uint4 state = new uint4(Prime1 + Prime2, Prime2, 0, (uint)-Prime1) + seed;
|
var state = new uint4(Prime1 + Prime2, Prime2, 0, (uint)-Prime1) + seed;
|
||||||
|
|
||||||
var count = numBytes >> 4;
|
var count = numBytes >> 4;
|
||||||
for (var i = 0; i < count; ++i)
|
for (var i = 0; i < count; ++i)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ internal unsafe struct NoiseJobVector : IJobParallel
|
|||||||
|
|
||||||
public void Execute(int startIndex, int endIndex, ref readonly JobExecutionContext ctx)
|
public void Execute(int startIndex, int endIndex, ref readonly JobExecutionContext ctx)
|
||||||
{
|
{
|
||||||
for (int i = startIndex; i < endIndex; i++)
|
for (var i = startIndex; i < endIndex; i++)
|
||||||
{
|
{
|
||||||
var x = i % width;
|
var x = i % width;
|
||||||
var y = i / height;
|
var y = i / height;
|
||||||
@@ -86,7 +86,7 @@ internal unsafe struct NoiseJobMath : IJobParallel
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static float2 GradientNoiseDirect(float2 uv)
|
private static float2 GradientNoiseDirect(float2 uv)
|
||||||
{
|
{
|
||||||
uv = noise.mod289(uv);
|
uv = noise.mod289(uv);
|
||||||
var x = (34 * uv.x + 1) * noise.mod289(uv.x) + uv.y;
|
var x = (34 * uv.x + 1) * noise.mod289(uv.x) + uv.y;
|
||||||
x = (34 * x + 1) * noise.mod289(x);
|
x = (34 * x + 1) * noise.mod289(x);
|
||||||
x = math.frac(x / 41) * 2 - 1;
|
x = math.frac(x / 41) * 2 - 1;
|
||||||
@@ -201,7 +201,7 @@ internal unsafe struct NoiseJobMathV : IJobParallel
|
|||||||
|
|
||||||
public void Execute(int startIndex, int endIndex, ref readonly JobExecutionContext ctx)
|
public void Execute(int startIndex, int endIndex, ref readonly JobExecutionContext ctx)
|
||||||
{
|
{
|
||||||
for (int i = startIndex; i < endIndex; i++)
|
for (var i = startIndex; i < endIndex; i++)
|
||||||
{
|
{
|
||||||
var baseIndex = i * 8;
|
var baseIndex = i * 8;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DefineConstants>$(DefineConstants);ENABLE_COLLECTION_CHECKS;PLATFORM_WINDOWS</DefineConstants>
|
<DefineConstants>$(DefineConstants);PLATFORM_WINDOWS;ENABLE_SAFETY_CHECKS</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
|
||||||
|
|
||||||
//BenchmarkRunner.Run<SPMDBenchmark>();
|
//BenchmarkRunner.Run<SPMDBenchmark>();
|
||||||
//var hashMap = new UnsafeHashMap<int, int>(10, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
//var hashMap = new UnsafeHashMap<int, int>(10, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Test.UnitTest.Buffer;
|
namespace Misaki.HighPerformance.Test.UnitTest.Buffer;
|
||||||
|
|
||||||
@@ -12,9 +11,9 @@ public unsafe class TestFreeList
|
|||||||
using var freeList = new FreeList(8, 1024);
|
using var freeList = new FreeList(8, 1024);
|
||||||
|
|
||||||
// Allocate various sizes
|
// Allocate various sizes
|
||||||
void* p1 = freeList.Allocate(16, 8);
|
var p1 = freeList.Allocate(16, 8);
|
||||||
void* p2 = freeList.Allocate(32, 8);
|
var p2 = freeList.Allocate(32, 8);
|
||||||
void* p3 = freeList.Allocate(64, 8);
|
var p3 = freeList.Allocate(64, 8);
|
||||||
|
|
||||||
Assert.IsTrue(p1 != null);
|
Assert.IsTrue(p1 != null);
|
||||||
Assert.IsTrue(p2 != null);
|
Assert.IsTrue(p2 != null);
|
||||||
@@ -26,8 +25,8 @@ public unsafe class TestFreeList
|
|||||||
freeList.Free(p3);
|
freeList.Free(p3);
|
||||||
|
|
||||||
// Allocate again - should reuse from buckets (or at least succeed)
|
// Allocate again - should reuse from buckets (or at least succeed)
|
||||||
void* p4 = freeList.Allocate(16, 8);
|
var p4 = freeList.Allocate(16, 8);
|
||||||
void* p5 = freeList.Allocate(32, 8);
|
var p5 = freeList.Allocate(32, 8);
|
||||||
|
|
||||||
Assert.IsTrue(p4 != null);
|
Assert.IsTrue(p4 != null);
|
||||||
Assert.IsTrue(p5 != null);
|
Assert.IsTrue(p5 != null);
|
||||||
@@ -44,21 +43,23 @@ public unsafe class TestFreeList
|
|||||||
using var freeList = new FreeList(8, 64 * 1024, threadCount);
|
using var freeList = new FreeList(8, 64 * 1024, threadCount);
|
||||||
|
|
||||||
var threads = new Thread[threadCount];
|
var threads = new Thread[threadCount];
|
||||||
for (int i = 0; i < threadCount; i++)
|
for (var i = 0; i < threadCount; i++)
|
||||||
{
|
{
|
||||||
threads[i] = new Thread(() =>
|
threads[i] = new Thread(() =>
|
||||||
{
|
{
|
||||||
for (int j = 0; j < iterations; j++)
|
for (var j = 0; j < iterations; j++)
|
||||||
{
|
{
|
||||||
void* ptr = freeList.Allocate(16, 8);
|
var ptr = freeList.Allocate(16, 8);
|
||||||
Assert.IsTrue(ptr != null);
|
Assert.IsTrue(ptr != null);
|
||||||
freeList.Free(ptr);
|
freeList.Free(ptr);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var t in threads) t.Start();
|
foreach (var t in threads)
|
||||||
foreach (var t in threads) t.Join();
|
t.Start();
|
||||||
|
foreach (var t in threads)
|
||||||
|
t.Join();
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -73,22 +74,22 @@ public unsafe class TestFreeList
|
|||||||
var producers = new Thread[producerCount];
|
var producers = new Thread[producerCount];
|
||||||
var consumers = new Thread[consumerCount];
|
var consumers = new Thread[consumerCount];
|
||||||
|
|
||||||
bool producing = true;
|
var producing = true;
|
||||||
|
|
||||||
for (int i = 0; i < producerCount; i++)
|
for (var i = 0; i < producerCount; i++)
|
||||||
{
|
{
|
||||||
producers[i] = new Thread(() =>
|
producers[i] = new Thread(() =>
|
||||||
{
|
{
|
||||||
for (int j = 0; j < iterations; j++)
|
for (var j = 0; j < iterations; j++)
|
||||||
{
|
{
|
||||||
void* ptr = freeList.Allocate(32, 8);
|
var ptr = freeList.Allocate(32, 8);
|
||||||
Assert.IsTrue(ptr != null);
|
Assert.IsTrue(ptr != null);
|
||||||
queue.Enqueue((IntPtr)ptr);
|
queue.Enqueue((IntPtr)ptr);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < consumerCount; i++)
|
for (var i = 0; i < consumerCount; i++)
|
||||||
{
|
{
|
||||||
consumers[i] = new Thread(() =>
|
consumers[i] = new Thread(() =>
|
||||||
{
|
{
|
||||||
@@ -106,12 +107,16 @@ public unsafe class TestFreeList
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var t in producers) t.Start();
|
foreach (var t in producers)
|
||||||
foreach (var t in consumers) t.Start();
|
t.Start();
|
||||||
|
foreach (var t in consumers)
|
||||||
|
t.Start();
|
||||||
|
|
||||||
foreach (var t in producers) t.Join();
|
foreach (var t in producers)
|
||||||
|
t.Join();
|
||||||
Volatile.Write(ref producing, false);
|
Volatile.Write(ref producing, false);
|
||||||
foreach (var t in consumers) t.Join();
|
foreach (var t in consumers)
|
||||||
|
t.Join();
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -122,18 +127,20 @@ public unsafe class TestFreeList
|
|||||||
using var freeList = new FreeList(8, 1024, 1);
|
using var freeList = new FreeList(8, 1024, 1);
|
||||||
|
|
||||||
var threads = new Thread[threadCount];
|
var threads = new Thread[threadCount];
|
||||||
for (int i = 0; i < threadCount; i++)
|
for (var i = 0; i < threadCount; i++)
|
||||||
{
|
{
|
||||||
threads[i] = new Thread(() =>
|
threads[i] = new Thread(() =>
|
||||||
{
|
{
|
||||||
void* ptr = freeList.Allocate(16, 8);
|
var ptr = freeList.Allocate(16, 8);
|
||||||
Assert.IsTrue(ptr != null);
|
Assert.IsTrue(ptr != null);
|
||||||
freeList.Free(ptr);
|
freeList.Free(ptr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var t in threads) t.Start();
|
foreach (var t in threads)
|
||||||
foreach (var t in threads) t.Join();
|
t.Start();
|
||||||
|
foreach (var t in threads)
|
||||||
|
t.Join();
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -143,7 +150,7 @@ public unsafe class TestFreeList
|
|||||||
|
|
||||||
// Allocate larger than default chunk size
|
// Allocate larger than default chunk size
|
||||||
nuint largeSize = 2048;
|
nuint largeSize = 2048;
|
||||||
void* ptr = freeList.Allocate(largeSize, 8);
|
var ptr = freeList.Allocate(largeSize, 8);
|
||||||
Assert.IsTrue(ptr != null);
|
Assert.IsTrue(ptr != null);
|
||||||
|
|
||||||
freeList.Free(ptr);
|
freeList.Free(ptr);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class TestConcurrentSlotMap
|
|||||||
{
|
{
|
||||||
var slotIndex = _slotMap.Add(200, out var generation);
|
var slotIndex = _slotMap.Add(200, out var generation);
|
||||||
Assert.IsTrue(_slotMap.Contains(slotIndex, generation));
|
Assert.IsTrue(_slotMap.Contains(slotIndex, generation));
|
||||||
var removed = _slotMap.Remove(slotIndex, generation + 1); // Wrong generation
|
var removed = _slotMap.Remove(slotIndex, generation + 1); // Wrong Generation
|
||||||
Assert.IsFalse(removed);
|
Assert.IsFalse(removed);
|
||||||
Assert.IsTrue(_slotMap.Contains(slotIndex, generation));
|
Assert.IsTrue(_slotMap.Contains(slotIndex, generation));
|
||||||
}
|
}
|
||||||
@@ -71,11 +71,11 @@ public class TestConcurrentSlotMap
|
|||||||
const int itemsPerThread = 1000;
|
const int itemsPerThread = 1000;
|
||||||
var tasks = new List<Task>();
|
var tasks = new List<Task>();
|
||||||
|
|
||||||
for (int t = 0; t < threadCount; t++)
|
for (var t = 0; t < threadCount; t++)
|
||||||
{
|
{
|
||||||
tasks.Add(Task.Run(() =>
|
tasks.Add(Task.Run(() =>
|
||||||
{
|
{
|
||||||
for (int i = 0; i < itemsPerThread; i++)
|
for (var i = 0; i < itemsPerThread; i++)
|
||||||
{
|
{
|
||||||
_slotMap.Add(i, out _);
|
_slotMap.Add(i, out _);
|
||||||
}
|
}
|
||||||
@@ -98,11 +98,11 @@ public class TestConcurrentSlotMap
|
|||||||
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
|
|
||||||
for (int t = 0; t < threadCount; t++)
|
for (var t = 0; t < threadCount; t++)
|
||||||
{
|
{
|
||||||
tasks.Add(Task.Run(() =>
|
tasks.Add(Task.Run(() =>
|
||||||
{
|
{
|
||||||
for (int i = 0; i < operationsPerThread; i++)
|
for (var i = 0; i < operationsPerThread; i++)
|
||||||
{
|
{
|
||||||
if (rand.NextDouble() < 0.5)
|
if (rand.NextDouble() < 0.5)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Misaki.HighPerformance.Collections;
|
using Misaki.HighPerformance.Collections;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Test.UnitTest.Collections;
|
namespace Misaki.HighPerformance.Test.UnitTest.Collections;
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ public class TestSlotMap
|
|||||||
{
|
{
|
||||||
var slotIndex = _slotMap.Add(200, out var generation);
|
var slotIndex = _slotMap.Add(200, out var generation);
|
||||||
Assert.IsTrue(_slotMap.Contains(slotIndex, generation));
|
Assert.IsTrue(_slotMap.Contains(slotIndex, generation));
|
||||||
var removed = _slotMap.Remove(slotIndex, generation + 1); // Wrong generation
|
var removed = _slotMap.Remove(slotIndex, generation + 1); // Wrong Generation
|
||||||
Assert.IsFalse(removed);
|
Assert.IsFalse(removed);
|
||||||
Assert.IsTrue(_slotMap.Contains(slotIndex, generation));
|
Assert.IsTrue(_slotMap.Contains(slotIndex, generation));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ public class TestUnsafeArray
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestIndexAccess()
|
public void TestIndexAccess()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _arr.Count; i++)
|
for (var i = 0; i < _arr.Count; i++)
|
||||||
{
|
{
|
||||||
_arr[i] = i * 10;
|
_arr[i] = i * 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < _arr.Count; i++)
|
for (var i = 0; i < _arr.Count; i++)
|
||||||
{
|
{
|
||||||
Assert.AreEqual(i * 10, _arr[i]);
|
Assert.AreEqual(i * 10, _arr[i]);
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ public class TestUnsafeArray
|
|||||||
{
|
{
|
||||||
_arr.Clear();
|
_arr.Clear();
|
||||||
|
|
||||||
int expectedValue = 0;
|
var expectedValue = 0;
|
||||||
foreach (var item in _arr)
|
foreach (var item in _arr)
|
||||||
{
|
{
|
||||||
Assert.AreEqual(expectedValue, item);
|
Assert.AreEqual(expectedValue, item);
|
||||||
|
|||||||
@@ -23,14 +23,14 @@ public class TestUnsafeStack
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestPushPop()
|
public void TestPushPop()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; i++)
|
for (var i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
_stack.Push(i);
|
_stack.Push(i);
|
||||||
}
|
}
|
||||||
Assert.AreEqual(10, _stack.Count);
|
Assert.AreEqual(10, _stack.Count);
|
||||||
for (int i = 9; i >= 0; i--)
|
for (var i = 9; i >= 0; i--)
|
||||||
{
|
{
|
||||||
int value = _stack.Pop();
|
var value = _stack.Pop();
|
||||||
Assert.AreEqual(i, value);
|
Assert.AreEqual(i, value);
|
||||||
}
|
}
|
||||||
Assert.AreEqual(0, _stack.Count);
|
Assert.AreEqual(0, _stack.Count);
|
||||||
@@ -40,7 +40,7 @@ public class TestUnsafeStack
|
|||||||
public void TestPeek()
|
public void TestPeek()
|
||||||
{
|
{
|
||||||
_stack.Push(42);
|
_stack.Push(42);
|
||||||
int value = _stack.Peek();
|
var value = _stack.Peek();
|
||||||
Assert.AreEqual(42, value);
|
Assert.AreEqual(42, value);
|
||||||
Assert.AreEqual(1, _stack.Count);
|
Assert.AreEqual(1, _stack.Count);
|
||||||
}
|
}
|
||||||
@@ -48,12 +48,12 @@ public class TestUnsafeStack
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestEnumeration()
|
public void TestEnumeration()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 5; i++)
|
for (var i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
_stack.Push(i);
|
_stack.Push(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
int expected = 4;
|
var expected = 4;
|
||||||
foreach (var item in _stack)
|
foreach (var item in _stack)
|
||||||
{
|
{
|
||||||
Assert.AreEqual(expected, item);
|
Assert.AreEqual(expected, item);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public static class CompressStoreTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe static void TestPattern_Double(double[] input, bool[] keepPattern)
|
private static unsafe void TestPattern_Double(double[] input, bool[] keepPattern)
|
||||||
{
|
{
|
||||||
// 1. Setup Input Vector
|
// 1. Setup Input Vector
|
||||||
// Handle case where Vector<TLane> is smaller than 8 (e.g. 2 or 4)
|
// Handle case where Vector<TLane> is smaller than 8 (e.g. 2 or 4)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Misaki.HighPerformance.LowLevel.Collections;
|
|||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using Misaki.HighPerformance.Mathematics.SPMD;
|
using Misaki.HighPerformance.Mathematics.SPMD;
|
||||||
using Misaki.HighPerformance.Test.Jobs;
|
using Misaki.HighPerformance.Test.Jobs;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Misaki.HighPerformance.Test.UnitTest.Jobs;
|
namespace Misaki.HighPerformance.Test.UnitTest.Jobs;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user