# UnmanagedMMU UnmanagedMMU is a high-performance C# memory manager library that provides efficient unmanaged memory allocation. --- ## Table of Contents - [IMemoryHandle](#imemoryhandle) - [IMemoryHandle (Untyped)](#imemoryhandle-untyped) - [IMemoryHandle\ (Typed)](#imemoryhandlet-typed) - [SegmentedPool](#segmentedpool) - [Segments](#segments) - [SegmentAlignment](#segmentalignment) - [Allocation Strategy](#allocation-strategy) - [Constructor](#constructor) - [Allocation Methods](#allocation-methods) - [Allocate](#allocate) - [AllocateAligned](#allocatealigned) - [Pool State Properties](#pool-state-properties) - [CurrentSegmentSize](#currentsegmentsize) - [TotalAllocatedBytes](#totalallocatedbytes) - [TotalUsedBytes](#totalusedbytes) - [ActiveSegmentCount](#activesegmentcount) - [FreeSegmentCount](#freesegmentcount) - [IsDisposed](#isdisposed) - [Segment Size Management](#segment-size-management) - [SetSegmentSize](#setsegmentsize) - [ResetSegmentSize](#resetsegmentsize) - [Reset and Trim](#reset-and-trim) - [Reset](#reset) - [Trim](#trim) - [Diagnostics](#diagnostics) - [GetPoolState](#getpoolstate) - [GetDiagnosticReport](#getdiagnosticreport) - [GetCurrentSegmentInfo](#getcurrentsegmentinfo) - [GetAllSegmentInfos](#getallsegmentinfos) - [Dispose](#dispose) --- ## IMemoryHandle The `UnmanagedMMU` namespace provides two interface variations for memory handles. The non-generic IMemoryHandle is a thin wrapper around a pointer for untyped access. The generic IMemoryHandle extends the non-generic interface to provide strong typing and implements IDisposable for automatic resource management. --- ### IMemoryHandle (Untyped) The **IMemoryHandle** interface represents a non-generic memory handle. It provides access to the raw pointer and byte count, useful when the element type is unknown at compile time or when interfacing with low-level APIs. **Key Properties:** | Property | Type | Description | | ----------- | ------- | --------------------------------------------- | | `Pointer` | `void*` | A raw pointer to the start of the allocation. | | `ByteCount` | `nuint` | The total size of the allocation in bytes. | ### IMemoryHandle\ (Typed) The **IMemoryHandle\** interface extends [IMemoryHandle](#IMemoryHandle) and provides strongly typed access to the underlying memory. It is the primary interface used with SegmentedPool allocations. This interface implements IDisposable. **Key Properties:** | Property | Type | Description | | ----------- | ------- | --------------------------------------------------------------------- | | `Pointer` | `T*` | A typed pointer to the start of the allocation. | | `ByteCount` | `nuint` | The total size of the allocation in bytes. | | `Length` | `nuint` | The number of `T` elements in the allocation (ByteCount / sizeof(T)). | **Example Usage:** ```csharp using var pool = new SegmentedPool(); // Allocate memory for 100 integers IMemoryHandle handle = pool.Allocate(100); try { // Access via typed pointer unsafe { for (int i = 0; i < handle.Length; i++) { handle.Pointer[i] = i; } } } finally { handle.Dispose(); } ``` --- ## SegmentedPool `SegmentedPool` is a **segmented bump allocator** for unmanaged memory in C#. It pre-allocates memory in **fixed-size segments** and serves allocations sequentially within each segment. Once a segment is full, the pool automatically switches to a new segment, allowing fast, contiguous allocations without fragmentation. Advantages of the `SegmentedPool`: - **High performance**: Allocation is simple pointer arithmetic. - **Contiguous memory**: Reduces cache misses and improves data locality. - **Thread-safe**: Supports concurrent allocation operations. - **Manual memory control**: Works outside of .NET GC, ideal for high-performance or low-latency scenarios. - **Configurable alignment**: Supports SIMD and hardware-specific alignment requirements. --- ### Segments A **Segment** is a contiguous block of unmanaged memory managed by the pool. Each segment contains: | Field | Type | Description | | -------- | ------- | ------------------------------------------------------------------------------- | | `Ptr` | `byte*` | Pointer to the start of the unmanaged memory block. | | `Offset` | `nuint` | Current allocation offset within the segment. Increases as memory is allocated. | | `Size` | `nuint` | Total size of the segment in bytes. | Segments are allocated automatically by the pool and should **never be modified outside the pool**. --- ### SegmentAlignment The `SegmentAlignment` enum defines the alignment requirements for memory segments. Values are powers of 2 and reflect common hardware requirements (SIMD, cache lines, native pointer size). | Value | Alignment | Description | | ------------ | --------- | ------------------------------------------------------------------------------------------------------------------------- | | `Aligned8` | 8 bytes | Minimum for 64-bit pointers and primitives (long, double). | | `Aligned16` | 16 bytes | Required for Vector128 (SSE/NEON). Common default for general-purpose SIMD workloads. | | `Aligned32` | 32 bytes | Required for Vector256 (AVX). Recommended default for SIMD-heavy applications. | | `Aligned64` | 64 bytes | Matches standard CPU cache-line size. Ensures segment bases align to cache line boundaries, minimizing cache-line splits. | | `Aligned128` | 128 bytes | Advanced optimization for specific cache-aware algorithms or AVX-512 contexts. | **Default**: `SegmentAlignment.Aligned32` --- ### Allocation Strategy The `SegmentedPool` uses a **bump allocator** strategy: 1. Memory is allocated sequentially within the current [Segments](#segments). 2. `Offset` is incremented with each allocation. 3. When the current segment does not have enough space, the pool switches to a new [Segments](#segments) (either from the free pool or a freshly allocated one). 4. Allocations are aligned according to the [SegmentAlignment](#segmentalignment) or a requested alignment. This provides **O(1) allocation performance** for most operations. --- ### Constructor Initializes a new instance of the SegmentedPool with the specified segment size, alignment, and number of pre-allocated [Segments](#segments). #### Syntax ```csharp // Default configuration SegmentedPool pool = new SegmentedPool(); // Custom parameters SegmentedPool pool = new SegmentedPool( segmentSize: 4 * 1024 * 1024, segmentAlignment: SegmentAlignment.Aligned32, initialSegments: 4, zeroMemory: false ); ``` #### Parameters | Parameter | Type | Description | | ------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `segmentSize` | `nuint` | Size of each [Segment](#segments) in bytes. Optional. Defaults to 4 MiB (4 * 1024 * 1024 bytes). | | `segmentAlignment` | `SegmentAlignment` | [Alignment](#segmentalignment) requirement for each allocated [Segment](#segments). Must be a power of 2. Optional. Defaults to **Aligned32**. | | `initialSegments` | `int` | Number of [Segments](#segments) to pre-allocate in the pool. Optional. Defaults to 4. | | `zeroMemory` | `bool` | When true, memory returned from the pool is zero-initialized. When false, memory may contain previously used data and it is the caller's responsibility to clear it if required. Optional. Defaults to false. | #### Return value Returns a new instance of the SegmentedPool with the specified [Segments](#segments) size and number of pre-allocated [Segments](#segments). #### Exceptions | Exception | Condition | | ----------------------------- | -------------------------------------------------------------------- | | `ArgumentOutOfRangeException` | Thrown if `segmentSize` is zero or `initialSegments` is less than 1. | | `ArgumentException` | Thrown if `alignment` is not a power of 2. | #### Remarks The SegmentedPool pre-allocates the specified number of [Segments](#segments) during construction, ensuring that the pool can immediately serve allocations without additional memory allocation overhead. The pool operates in unmanaged memory and bypasses the .NET garbage collector, so all memory must be manually released by calling Dispose() when the pool is no longer needed. When `zeroMemory` is set to true, all memory returned from the pool is zero-initialized, which ensures that no residual data from previous allocations is exposed. When `zeroMemory` is false, memory may contain leftover data from prior use, and it is the caller's responsibility to clear it if necessary to maintain data integrity or security. Setting `zeroMemory` to true may have a slight performance cost due to the additional initialization step. --- ### Allocation Methods The SegmentedPool provides two allocation methods, both returning [IMemoryHandle\](#IMemoryHandleT) for safe memory management. #### Allocate Allocates a block of unmanaged memory of the specified count for elements of type `T` using the pool's default segment alignment. ##### Syntax ```csharp IMemoryHandle handle = pool.Allocate(count); ``` ##### Parameters | Parameter | Type | Description | | --------- | ----- | ---------------------------------------------------------------------- | | `count` | `int` | Number of elements of type `T` to allocate. Must be greater than zero. | ##### Return value An [IMemoryHandle\](#IMemoryHandleT) representing the allocated memory. The handle is valid until either [Reset](#segmentedpool-reset) or [Dispose](#segmentedpool-dispose) is called on the pool. ##### Remarks - This allocation is performed in unmanaged memory and bypasses the .NET garbage collector. - Accessing the memory after [Reset](#segmentedpool-reset) or [Dispose](#segmentedpool-dispose) has been called is undefined behavior. - Memory alignment is based on the pool's configured [SegmentAlignment](#segmentalignment). ##### Exceptions | Exception | Condition | | ----------------------------- | ----------------------------------------------------------------------- | | `ArgumentOutOfRangeException` | Thrown if `count` is less than or equal to zero. | | `OverflowException` | Thrown if the total allocation size exceeds the maximum allowable size. | | `ObjectDisposedException` | Thrown if the pool has been disposed. | ##### Example ```csharp using var pool = new SegmentedPool(); IMemoryHandle handle = pool.Allocate(100); try { // Access memory through typed pointer unsafe { for (int i = 0; i < handle.Length; i++) { handle.Pointer[i] = i; } } } finally { handle.Dispose(); // Returns memory to pool } ``` --- #### AllocateAligned Allocates a block of unmanaged memory with the specified alignment requirement. This is useful for SIMD or hardware-specific operations. ##### Syntax ```csharp IMemoryHandle handle = pool.AllocateAligned(count, alignment); ``` ##### Parameters | Parameter | Type | Description | | ----------- | ------------------ | --------------------------------------------------------------------------------------------------------------- | | `count` | `int` | Number of elements of type `T` to allocate. Must be greater than zero. | | `alignment` | `SegmentAlignment` | The [alignment](#segmentalignment) to align the allocation to inside the currently active [Segment](#segments). | ##### Return value An [IMemoryHandle\](#IMemoryHandleT) representing the allocated memory. The handle is valid until either [Reset](#segmentedpool-reset) or [Dispose](#segmentedpool-dispose) is called on the pool. ##### Remarks - This allocation is performed in unmanaged memory and bypasses the .NET garbage collector. - Accessing the memory after [Reset](#segmentedpool-reset) or [Dispose](#segmentedpool-dispose) has been called is undefined behavior. - The alignment requirement must be a power of 2. - If the requested alignment is less than `sizeof(T)`, the type's natural size is used instead. ##### Exceptions | Exception | Condition | | ----------------------------- | ----------------------------------------------------------------------- | | `ArgumentOutOfRangeException` | Thrown if `count` is less than or equal to zero. | | `OverflowException` | Thrown if the total allocation size exceeds the maximum allowable size. | | `ObjectDisposedException` | Thrown if the pool has been disposed. | | `ArgumentException` | Thrown if `alignment` is not a power of 2. | ##### Example ```csharp using var pool = new SegmentedPool(segmentAlignment: SegmentAlignment.Aligned32); IMemoryHandle handle = pool.AllocateAligned(64, SegmentAlignment.Aligned32); try { unsafe { // Perform SIMD operations on aligned memory for (int i = 0; i < handle.Length; i++) { // Process aligned SIMD data } } } finally { handle.Dispose(); } ``` --- ### Pool State Properties The SegmentedPool provides several read-only properties for monitoring pool state: #### CurrentSegmentSize Gets the size, in bytes, used when allocating new [Segment](#segments) instances. ##### Syntax ```csharp nuint size = pool.CurrentSegmentSize; ``` ##### Return value The size, in bytes, used when allocating new [Segment](#segments). ##### Remarks This reflects the most recently configured size and affects only future [segment](#segments) allocations. --- #### TotalAllocatedBytes Gets the total number of bytes that have currently been allocated from the system. ##### Syntax ```csharp nuint allocated = pool.TotalAllocatedBytes; ``` ##### Return value The total number of bytes allocated from the system. ##### Remarks This includes memory for all active and free [Segments](#segments). --- #### TotalUsedBytes Gets the total number of bytes currently in use across all active [segments](#segments). ##### Syntax ```csharp nuint used = pool.TotalUsedBytes; ``` ##### Return value The total number of bytes currently in use. ##### Remarks This represents the actual data bytes allocated, excluding alignment padding. #### ActiveSegmentCount Gets the number of [segments](#segments) currently in use in the pool. ##### Syntax ```csharp int count = pool.ActiveSegmentCount; ``` ##### Return value The number of [segments](#segments) that are currently active and not yet returned to the free pool. ##### Remarks This property is thread-safe and reflects the current state of the pool. --- #### FreeSegmentCount Gets the number of free [segments](#segments) available for reuse in the pool. ##### Syntax ```csharp int count = pool.FreeSegmentCount; ``` ##### Return value The number of [segments](#segments) that are currently free and available for reuse. ##### Remarks This property is thread-safe. A value of 0 indicates that the pool will need to allocate a new [segment](#segments) for the next allocation. --- #### IsDisposed Gets a value indicating whether this instance has been disposed. ##### Syntax ```csharp bool isDisposed = pool.IsDisposed; ``` ##### Return value `true` if the pool has been disposed; otherwise `false`. ##### Remarks Once this value is true, any further calls to allocation or management methods will throw a `ObjectDisposedException`. --- ### Segment Size Management #### SetSegmentSize Sets the [Segment](#segments) size to use for future allocations. ##### Syntax ```csharp pool.SetSegmentSize(newSize); ``` ##### Parameters | Parameter | Type | Description | | --------- | ----- | ------------------------------------------- | | `newSize` | nuint | The new segment size in bytes. Must be > 0. | ##### Return value None. ##### Remarks This method will only affect the [Segment](#segments) size of future allocations; it does not modify existing [Segments](#segments). ##### Exceptions | Exception | Condition | | ----------------------------- | ------------------------------------- | | `ArgumentOutOfRangeException` | Thrown if `newSize` is zero. | | `ObjectDisposedException` | Thrown if the pool has been disposed. | --- #### ResetSegmentSize Resets the [Segment](#segments) size for future allocations back to the default (4 MiB). ##### Syntax ```csharp pool.ResetSegmentSize(); ``` ##### Parameters None. ##### Return value None. ##### Remarks This method will only affect the [Segment](#segments) size of future allocations; it does not modify existing [Segments](#segments). --- ### Reset and Trim #### Reset Resets the pool, returning all active [Segments](#segments) to the free pool for reuse. ##### Syntax ```csharp pool.Reset(trim: false); ``` ##### Parameters | Parameter | Type | Description | | --------- | ------ | ----------------------------------------------------------------------------------------- | | `trim` | `bool` | Optional. If true, trims excess free [Segments](#segments) after reset. Defaults to false | ##### Return value None. ##### Remarks This method resets all active [Segments](#segments) offsets to zero. Additionally, no memory is freed unless trim is true; the pool retains free [Segments](#segments) for future allocations. --- #### Trim Frees unused [Segments](#segments) in the free pool, reducing memory usage. ##### Syntax ```csharp pool.Trim(minFreeSegments: 16); ``` ##### Parameters | Parameter | Type | Description | | ----------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------- | | `minFreeSegments` | `int` | Minimum number of free [Segments](#segments) to retain. [Segments](#segments) beyond this count are released. Defaults to 16. | ##### Return value None. ##### Remarks This method releases unmanaged memory from [Segments](#segments) beyond the specified minimum. This is useful for helping to control the unmanaged memory footprint in long-running applications. ##### Exceptions | Exception | Condition | | ----------------------------- | ---------------------------------------- | | `ArgumentOutOfRangeException` | Thrown if `minFreeSegments` is negative. | | `ObjectDisposedException` | Thrown if the pool has been disposed. | --- ### Diagnostics The SegmentedPool provides several diagnostic methods for monitoring pool health and efficiency. #### GetPoolState Gets a snapshot of the current pool state for diagnostics. ##### Syntax ```csharp SegmentedPool.PoolState state = pool.GetPoolState(); ``` ##### Return value A `SegmentedPool.PoolState` struct containing current pool metrics. ##### Example ```csharp var state = pool.GetPoolState(); Console.WriteLine($"Active: {state.ActiveSegmentCount}, Free: {state.FreeSegmentCount}"); Console.WriteLine($"Efficiency: {(100.0 * state.TotalUsed / (state.TotalUsed + state.PaddingBytes)):F0}%"); ``` --- #### GetDiagnosticReport Generates a diagnostic report for the pool including actionable suggestions. ##### Syntax ```csharp string report = pool.GetDiagnosticReport(); ``` ##### Return value A formatted diagnostic string. ##### Example ```csharp string report = pool.GetDiagnosticReport(); // Output: // === SegmentedPool Diagnostics === // Configuration // Segment Alignment: 32 bytes (Min Base Alignment) // Segment Size: 4 MiB // Segment Summary // Total Segments: 5 (4 active, 1 free) // Potential Savings: 0 (via Trim()) // Current Segment // Base Address: 0x1a2b3c4d // Offset: 1048576 bytes // Base Alignment: OK (To 32 bytes) // Memory Statistics // Total Reserved: 16 MiB // Total Used: 8 MiB // Efficiency: 80% (Good) // Padding Overhead: 2 MiB // Allocation Breakdown // Data Bytes: 8 MiB // Alignment Padding: 2 MiB // Total Segment Space: 10 MiB // Action Required: Pool operating normally. ``` --- #### GetCurrentSegmentInfo Gets information about the currently active segment. This is the primary diagnostic view for memory usage within the active segment. ##### Syntax ```csharp SegmentedPool.SegmentInfo info = pool.GetCurrentSegmentInfo(); ``` ##### Return value A `SegmentedPool.SegmentInfo` struct for the current segment. --- #### GetAllSegmentInfos Gets a list of all segment details for deep diagnostics. Includes both active and free segments. ##### Syntax ```csharp List allSegments = pool.GetAllSegmentInfos(); ``` ##### Return value A list of `SegmentedPool.SegmentInfo` structs containing all segments. --- ### Dispose Releases all unmanaged memory used by the pool and clears internal state. #### Syntax ```csharp pool.Dispose(); ``` #### Parameters None. #### Return value None. #### Remarks This method frees all active and free [Segments](#segments) so that after disposal, any memory allocated from the pool is invalid. This method is safe to call multiple times and calling any other public method after disposal is also safe. After disposal, all allocations become invalid and any further operations will throw [ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception).