A C# library inspired by Zig's memory and context management, providing explicit memory control, multiple allocator types, and Zig-style patterns for .NET applications.
$ dotnet add package ZiggyAllocHigh-performance unmanaged memory management for .NET with explicit control and zero GC pressure.
ZiggyAlloc is a high-performance C# library for unmanaged memory management. It provides explicit control over memory allocation while maintaining safety through well-designed abstractions and automatic cleanup mechanisms.
UnmanagedBuffer<T> with bounds checkingusing statementsusing ZiggyAlloc;
// Create allocator
var allocator = new SystemMemoryAllocator();
// Allocate memory with automatic cleanup
using var buffer = allocator.Allocate<int>(1000);
// Use like a normal array with bounds checking
buffer[0] = 42;
int value = buffer[0];
// Convert to Span<T> for high-performance operations
Span<int> span = buffer;
span.Fill(123);
ZiggyAlloc provides significant performance improvements over traditional managed arrays, especially for large data sets:
| Data Type | Managed Array | Unmanaged Array | Performance Gain | GC Pressure |
|---|---|---|---|---|
byte | 5.85μs | 6.01μs | ~1.03x | High |
int | 5.65μs | 8.71μs | ~1.54x | High |
double | 9.40μs | 5.66μs | ~1.66x | High |
Point3D| 9.85μs |
| 6.13μs |
| ~1.61x |
| High |
Key Insight: While small allocations might be slightly slower, large data types (like
doublearrays) show significant performance improvements with unmanaged memory. Most importantly, unmanaged allocations eliminate GC pressure entirely.
Different allocators for different use cases:
| Allocator | Best For | Thread Safety | GC Pressure | Performance |
|---|---|---|---|---|
| SystemMemoryAllocator | General purpose | ✅ Safe | ❌ None | ⚡ High |
| ScopedMemoryAllocator | Temporary allocations | ❌ Not safe | ❌ None | ⚡⚡ Very High |
| DebugMemoryAllocator | Development/testing | ✅ Safe | ❌ None | ⚡ Medium |
| UnmanagedMemoryPool | Frequent allocations | ✅ Safe | ❌ None | ⚡⚡ Very High |
| HybridAllocator | Mixed workloads | ✅ Safe | ⚡ Adaptive | ⚡⚡ Very High |
| SlabAllocator | High-frequency small allocations | ✅ Safe | ❌ None | ⚡⚡ Very High |
graph TD
A[IUnmanagedMemoryAllocator] --> B[SystemMemoryAllocator]
A --> C[ScopedMemoryAllocator]
A --> D[DebugMemoryAllocator]
A --> E[UnmanagedMemoryPool]
A --> F[HybridAllocator]
A --> G[SlabAllocator]
B --> H[Native Memory]
C --> B
D --> B
E --> B
F --> B
G --> B
I[UnmanagedBuffer<T>] --> J[Bounds Checking]
I --> K[Automatic Cleanup]
I --> L[Span<T> Integration]
The core type for working with unmanaged memory:
var allocator = new SystemMemoryAllocator();
using var buffer = allocator.Allocate<int>(100);
// Type-safe access with bounds checking
buffer[0] = 42;
int value = buffer[99];
// Convert to Span<T> for high-performance operations
Span<int> span = buffer;
span.Fill(123);
Direct system memory allocation with tracking.
Arena-style allocator that frees all memory when disposed.
Tracks allocations and detects memory leaks with caller information.
Reduces allocation overhead by reusing previously allocated buffers.
Automatically chooses between managed and unmanaged allocation based on size and type for optimal performance.
A slab allocator that pre-allocates large blocks of memory and sub-allocates from them. This allocator is particularly efficient for scenarios with many small, similarly-sized allocations.
var systemAllocator = new SystemMemoryAllocator();
using var slabAllocator = new SlabAllocator(systemAllocator);
// Small allocations are served from pre-allocated slabs
using var smallBuffer = slabAllocator.Allocate<int>(100);
// Large allocations are delegated to the base allocator
using var largeBuffer = slabAllocator.Allocate<int>(10000);
Key Benefits:
Use Cases:
Reduce allocation overhead by reusing buffers:
var systemAllocator = new SystemMemoryAllocator();
using var pool = new UnmanagedMemoryPool(systemAllocator);
// First allocation - creates new buffer
using var buffer1 = pool.Allocate<int>(100);
// Second allocation - reuses buffer from pool if available
using var buffer2 = pool.Allocate<int>(100);
// Buffers are returned to the pool when disposed
Intelligent allocation strategy selection:
var systemAllocator = new SystemMemoryAllocator();
using var hybridAllocator = new HybridAllocator(systemAllocator);
// Small allocations may use managed arrays for better performance
using var smallBuffer = hybridAllocator.Allocate<int>(100);
// Large allocations will use unmanaged memory to avoid GC pressure
using var largeBuffer = hybridAllocator.Allocate<int>(10000);
Benchmarks show significant performance improvements over managed arrays for large data:
// Without pooling - each allocation calls into the OS
var allocator = new SystemMemoryAllocator();
for (int i = 0; i < 1000; i++)
{
using var buffer = allocator.Allocate<byte>(1024); // System call each time
// Process buffer...
}
// With pooling - first allocation per size calls OS, subsequent allocations reuse
using var pool = new UnmanagedMemoryPool(allocator);
for (int i = 0; i < 1000; i++)
{
using var buffer = pool.Allocate<byte>(1024); // Reuses pooled buffer
// Process buffer...
}
| Data Type | Managed Allocation | Unmanaged Allocation |
|---|---|---|
byte[] | ≤ 1,024 elements | > 1,024 elements |
int[] | ≤ 512 elements | > 512 elements |
double[] | ≤ 128 elements | > 128 elements |
structs | ≤ 64 elements | > 64 elements |
The examples directory contains organized examples demonstrating various use cases:
using statements for RAII-style memory managementTo run examples:
cd examples
dotnet run -- basic
dotnet run -- allocators
dotnet run -- performance
dotnet run -- realworld
Install the NuGet package:
dotnet add package ZiggyAlloc
Or add to your .csproj:
<PackageReference Include="ZiggyAlloc" Version="1.2.5" />
unsafe code enabled (configured in package)This project is licensed under the MIT License - see the LICENSE file for details.