A contiguous, garbage-collected managed array for .NET with more than 2 billion elements.
BigArray<T>, BigMemory<T>, BigReadOnlyMemory<T>, BigSpan<T>, and BigReadOnlySpan<T> for .NET code that wants the Array/Memory/Span programming model with nint lengths and indexes.
The standard T[] and Span<T> APIs are excellent until the array you want to model is larger than the largest single managed array. Hezium.Memory keeps the surface area familiar: allocate an owner, take a span-like view, slice it, search it, copy it, sort it, and pass references around without inventing a second indexing style.
dotnet add package Hezium.Memoryusing Hezium.Memory;
nint length = 10_000_000_000;
BigArray<byte> buffer = new(length);
buffer[0] = 1;
buffer[5_000_000_000] = 2;
buffer[length - 1] = 3;
BigSpan<byte> window = buffer.AsBigSpan(5_000_000_000, 1024);
window[0] = 42;
Console.WriteLine(buffer[5_000_000_000]); // 42BigArray<T> stores its elements in one contiguous region of managed memory, even when its length exceeds Array.MaxLength. The storage is GC-managed, so reference-type elements are tracked and the allocation is collected when no longer used.
For explicit GC-style allocation, use GC.AllocateBigArray<T>() or GC.AllocateUninitializedBigArray<T>(). Both APIs accept the same pinned option as the built-in GC array allocation helpers.
BigArray<byte> zeroed = GC.AllocateBigArray<byte>(length);
BigArray<byte> scratch = GC.AllocateUninitializedBigArray<byte>(length);
BigArray<byte> pinned = GC.AllocateBigArray<byte>(length, pinned: true);For more details about how this library was built, see the Introduction.
Some workloads are naturally one-dimensional and very large: columnar data, precomputed lookup tables, native interop buffers, generated datasets, simulation state, and file-backed processing pipelines. The important part is not only "can I allocate a lot of elements", but "can I keep using the same mental model when I do?"
Hezium.Memory is built around that goal:
BigArray<T>is the owning storage type.BigMemory<T>is the mutable storable view.BigReadOnlyMemory<T>is the read-only storable view.BigSpan<T>is the mutable stack-only view.BigReadOnlySpan<T>is the read-only stack-only view.- Lengths, indexes, and offsets are
nint. - APIs intentionally resemble
Array,Memory<T>,ReadOnlyMemory<T>,Span<T>,ReadOnlySpan<T>, andMemoryMarshal.
BigArray<T> is a one-dimensional, zero-based collection with a length that can exceed Array.MaxLength.
using Hezium.Memory;
BigArray<int> array = new(10);
array.Fill(-1);
array[0] = 10;
array[array.Length - 1] = 90;
nint middle = array.IndexOf(-1);
bool hasNinety = array.Contains(90);
Console.WriteLine($"{array.Length}, {middle}, {hasNinety}");The maximum length depends on the element size:
nint byteCapacity = BigArray<byte>.MaxLength;
nint longCapacity = BigArray<long>.MaxLength;
Console.WriteLine(byteCapacity > Array.MaxLength);
Console.WriteLine(longCapacity > Array.MaxLength);Unlike a jagged array, a BigArray<T> does not divide its elements among multiple backing arrays. It remains contiguous beyond Array.MaxLength and exposes the entire allocation through one nint-indexed space.
BigArray<byte> buffer = new((nint)Array.MaxLength + 1024);
nint last = buffer.Length - 1;
buffer[last] = 255;
Console.WriteLine(buffer[last]);BigSpan<T> is a ref struct view over a contiguous region. It can be created from a BigArray<T>, a normal Span<T>, a reference, or a pointer.
using Hezium.Memory;
BigArray<int> owner = new(1024);
BigSpan<int> span = owner.AsBigSpan();
span.Fill(1);
BigSpan<int> block = span.Slice(128, 256);
block.Clear();
owner[128] = 123;
Console.WriteLine(block[0]); // 123The type is deliberately span-shaped: indexing returns by reference, slicing is cheap, foreach works, and GetPinnableReference() is available for pinning scenarios.
Span<int> small = stackalloc int[] { 1, 2, 3, 4 };
BigSpan<int> big = small;
foreach (ref int item in big)
{
item *= 2;
}BigReadOnlySpan<T> is the read-only counterpart. BigSpan<T> converts to it implicitly, and normal Span<T>/ReadOnlySpan<T> values can be used as small read-only big spans.
BigArray<int> data = new(5);
data.AsBigSpan().Fill(7);
BigReadOnlySpan<int> readOnly = data.AsBigSpan();
ref readonly int first = ref readOnly[0];
BigReadOnlySpan<int> tail = readOnly.Slice(1);
Console.WriteLine(first);
Console.WriteLine(tail.Length);For text, take an explicit int-sized window before creating a string:
BigReadOnlySpan<char> text = "hello".ToCharArray();
string middle = text.Slice(1, 3).ToSpan(0, 3).ToString();
Console.WriteLine(middle); // ellBigMemory<T> and BigReadOnlyMemory<T> mirror the storable Memory<T>/ReadOnlyMemory<T> shape with nint lengths. They can be sliced, copied, pinned, converted to arrays, and materialized as big spans when you need the hot by-ref path.
using Hezium.Memory;
BigArray<int> owner = new(1024);
BigMemory<int> memory = owner.AsBigMemory(128, 256);
memory.Span.Fill(7);
BigMemory<int> tail = memory.Slice(128);
BigReadOnlyMemory<int> readOnly = memory;
Console.WriteLine(tail.Length);
Console.WriteLine(readOnly.Span[0]);Normal arrays and array segments convert to big memory without copying:
int[] small = [1, 2, 3, 4];
BigMemory<int> memory = small;
BigReadOnlyMemory<int> window = new ArraySegment<int>(small, 1, 2);
memory.Span[2] = 30;
Console.WriteLine(window.Span[1]); // 30
Console.WriteLine(small[2]); // 30The extension methods follow the Span<T> vocabulary, but return nint where an index can be large.
BigArray<int> numbers = new(8);
BigSpan<int> span = numbers.AsBigSpan();
for (nint i = 0; i < span.Length; i++)
{
span[i] = (int)(i % 4);
}
nint firstTwo = span.IndexOf(2);
nint lastTwo = span.LastIndexOf(2);
bool startsWith = span.StartsWith(new[] { 0, 1 });
Console.WriteLine($"{firstTwo}, {lastTwo}, {startsWith}");Copying works between big memory, big spans, normal spans, and BigArray<T>:
BigArray<int> source = new(4);
source.AsBigSpan().Fill(9);
BigArray<int> destination = new(8);
source.CopyTo(destination, destinationIndex: 2);
int[] small = new int[4];
source.AsBigSpan().CopyTo(small);
BigMemory<int> memory = source.AsBigMemory();
BigMemory<int> memoryDestination = destination.AsBigMemory(2, 4);
memory.CopyTo(memoryDestination);Search, trim, split, compare, and sort operations are available where the underlying .NET span APIs support them:
BigSpan<int> values = new int[] { 0, 2, 1, 2, 0 };
BigSpan<int> trimmed = values.Trim(0);
nint marker = values.IndexOfAny(1, 9);
foreach (BigReadOnlySpan<int> segment in values.Split(2))
{
Console.WriteLine(segment.Length);
}
values.Sort();SearchValues<T> is supported for repeated searches over compatible element types:
using System.Buffers;
using Hezium.Memory;
BigReadOnlySpan<byte> bytes = new byte[] { 1, 2, 3, 2, 1 };
SearchValues<byte> separators = SearchValues.Create((ReadOnlySpan<byte>)[2, 9]);
nint firstSeparator = bytes.IndexOfAny(separators);
foreach (BigReadOnlySpan<byte> segment in bytes.SplitAny(separators))
{
Console.WriteLine(segment.Length);
}The maximum number of elements processed by the underlying span operations at once can be configured at application startup:
<ItemGroup>
<RuntimeHostConfigurationOption Include="Hezium.Memory.MaxProcessingChunkLength" Value="1048576" />
</ItemGroup>Hezium.Memory.MaxProcessingChunkLength defaults to Array.MaxLength. It lets applications tune the processing granularity of span-based algorithms for their workload; smaller chunks also introduce more per-chunk overhead. The value is process-wide, is read once, and does not change the BigArray<T> storage layout.
The configured value is clamped to the range from 65536 through Array.MaxLength.
MemoryMarshal extension members make it possible to create and inspect big spans from raw references.
using System.Runtime.InteropServices;
using Hezium.Memory;
int value = 42;
BigSpan<int> span = MemoryMarshal.CreateBigSpan(ref value, length: 1);
ref int reference = ref MemoryMarshal.GetReference(span);
reference++;
BigReadOnlySpan<int> readOnly = span;
ref readonly int readOnlyReference = ref MemoryMarshal.GetReference(readOnly);
Console.WriteLine(readOnlyReference); // 43| Type | Purpose |
|---|---|
BigArray<T> |
Owning storage with nint Length, MaxLength, indexer, enumeration, AsBigSpan, AsBigMemory, and AsSpan for int-sized windows. |
BigMemory<T> |
Mutable storable view with nint Length, slicing, Span, copy/try-copy, pinning, ToArray, and ToBigArray. |
BigReadOnlyMemory<T> |
Read-only storable view with nint Length, slicing, Span, copy/try-copy, pinning, ToArray, and ToBigArray. |
BigSpan<T> |
Mutable ref struct view with nint Length, slicing, by-ref indexing, pinning, enumeration, copy/search/trim/split/sort helpers. |
BigReadOnlySpan<T> |
Read-only ref struct view with slicing, by-readonly-ref indexing, pinning, enumeration, copy/search/trim/split helpers. |
MemoryMarshal extensions |
CreateBigSpan, GetReference(BigSpan<T>), and GetReference(BigReadOnlySpan<T>). |
GC extensions |
AllocateBigArray<T> and AllocateUninitializedBigArray<T> with optional pinned storage. |
- .NET 10 or later
BigMemory<T>andBigReadOnlyMemory<T>are regular structs that can be stored; theirSpanproperties produce stack-only big span views.BigSpan<T>andBigReadOnlySpan<T>areref structtypes, so they follow the same stack-only lifetime rules asSpan<T>.ToArray()requires the span or array to fit into a singleT[]; useToBigArray()when the result may exceedArray.MaxLength.BigArray<T>.MaxLengthis element-size dependent.- Value types larger than 65535 bytes are rejected.
These benchmarks compare BigArray<T> with T[][] (a jagged array) at equivalent logical lengths.
Environment:
- CPU: AMD EPYC 9V74 with 48 cores
- OS: Ubuntu 24.04.4 LTS (GNU/Linux 6.17.0-1018-azure x86_64)
- Memory: 192 GB
- .NET: 10.0.109
| Method | Job | Server | Length | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|---|---|---|---|---|---|---|---|---|---|---|
| JaggedArray | Job-MSAXQN | False | 1048576 | 107.77 μs | 0.579 μs | 0.513 μs | 333.2520 | 332.8857 | 332.8857 | 4 MB |
| BigArray | Job-MSAXQN | False | 1048576 | 109.14 μs | 1.923 μs | 1.705 μs | 330.5664 | 330.2002 | 330.2002 | 4 MB |
| JaggedArray | Job-WGBUQW | True | 1048576 | 130.19 μs | 2.523 μs | 3.454 μs | 3.1738 | 3.1738 | 3.1738 | 4 MB |
| BigArray | Job-WGBUQW | True | 1048576 | 127.78 μs | 2.468 μs | 2.937 μs | 3.4180 | 3.4180 | 3.4180 | 4 MB |
| JaggedArray | Job-MSAXQN | False | 4294967296 | 320,082.17 μs | 60,406.206 μs | 90,413.172 μs | 500.0000 | 500.0000 | 500.0000 | 16384 MB |
| BigArray | Job-MSAXQN | False | 4294967296 | 251,707.29 μs | 112.220 μs | 99.480 μs | 500.0000 | 500.0000 | 500.0000 | 16384.06 MB |
| JaggedArray | Job-WGBUQW | True | 4294967296 | 179.18 μs | 3.907 μs | 5.215 μs | - | - | - | 16384 MB |
| BigArray | Job-WGBUQW | True | 4294967296 | 40.13 μs | 4.645 μs | 6.953 μs | - | - | - | 16384.06 MB |
| Method | Job | Server | Length | Mean | Error | StdDev | Allocated |
|---|---|---|---|---|---|---|---|
| JaggedRandomLoad | Job-MSAXQN | False | 1048576 | 15.753 μs | 0.0074 μs | 0.0061 μs | - |
| BigArrayRandomLoad | Job-MSAXQN | False | 1048576 | 8.145 μs | 0.0628 μs | 0.0588 μs | - |
| JaggedRandomLoad | Job-WGBUQW | True | 1048576 | 16.092 μs | 0.0524 μs | 0.0490 μs | - |
| BigArrayRandomLoad | Job-WGBUQW | True | 1048576 | 7.952 μs | 0.1058 μs | 0.0990 μs | - |
| JaggedRandomLoad | Job-MSAXQN | False | 4294967296 | 26.112 μs | 0.0270 μs | 0.0239 μs | - |
| BigArrayRandomLoad | Job-MSAXQN | False | 4294967296 | 16.648 μs | 0.0171 μs | 0.0160 μs | - |
| JaggedRandomLoad | Job-WGBUQW | True | 4294967296 | 26.072 μs | 0.1456 μs | 0.1290 μs | - |
| BigArrayRandomLoad | Job-WGBUQW | True | 4294967296 | 16.198 μs | 0.0296 μs | 0.0277 μs | - |
| JaggedRandomStore | Job-MSAXQN | False | 1048576 | 16.459 μs | 0.0032 μs | 0.0029 μs | - |
| BigArrayRandomStore | Job-MSAXQN | False | 1048576 | 17.277 μs | 0.0098 μs | 0.0082 μs | - |
| JaggedRandomStore | Job-WGBUQW | True | 1048576 | 51.193 μs | 0.0789 μs | 0.0738 μs | - |
| BigArrayRandomStore | Job-WGBUQW | True | 1048576 | 17.200 μs | 0.0093 μs | 0.0087 μs | - |
| JaggedRandomStore | Job-MSAXQN | False | 4294967296 | 25.410 μs | 0.0359 μs | 0.0336 μs | - |
| BigArrayRandomStore | Job-MSAXQN | False | 4294967296 | 19.036 μs | 0.0148 μs | 0.0131 μs | - |
| JaggedRandomStore | Job-WGBUQW | True | 4294967296 | 26.077 μs | 0.0445 μs | 0.0416 μs | - |
| BigArrayRandomStore | Job-WGBUQW | True | 4294967296 | 18.947 μs | 0.0142 μs | 0.0126 μs | - |
| Method | Job | Server | Length | Mean | Error | StdDev | Allocated |
|---|---|---|---|---|---|---|---|
| JaggedIndexOfAny | Job-MSAXQN | False | 1048576 | 35,037.717 ns | 8.3021 ns | 7.7658 ns | - |
| BigArrayIndexOfAny | Job-MSAXQN | False | 1048576 | 34,034.869 ns | 114.1575 ns | 101.1977 ns | - |
| JaggedIndexOfAny | Job-WGBUQW | True | 1048576 | 33,834.867 ns | 636.5404 ns | 653.6801 ns | - |
| BigArrayIndexOfAny | Job-WGBUQW | True | 1048576 | 34,216.534 ns | 5.1605 ns | 4.8272 ns | - |
| JaggedIndexOfAny | Job-MSAXQN | False | 4294967296 | 149,024,225.036 ns | 72,304.7208 ns | 64,096.2517 ns | - |
| BigArrayIndexOfAny | Job-MSAXQN | False | 4294967296 | 149,208,981.917 ns | 148,120.4669 ns | 138,551.9767 ns | - |
| JaggedIndexOfAny | Job-WGBUQW | True | 4294967296 | 149,841,356.783 ns | 164,965.1421 ns | 154,308.4964 ns | - |
| BigArrayIndexOfAny | Job-WGBUQW | True | 4294967296 | 149,210,761.017 ns | 178,532.2659 ns | 166,999.1924 ns | - |
| JaggedLastIndexOfAny | Job-MSAXQN | False | 1048576 | 3.743 ns | 0.0041 ns | 0.0039 ns | - |
| BigArrayLastIndexOfAny | Job-MSAXQN | False | 1048576 | 3.622 ns | 0.0052 ns | 0.0049 ns | - |
| JaggedLastIndexOfAny | Job-WGBUQW | True | 1048576 | 3.710 ns | 0.0062 ns | 0.0058 ns | - |
| BigArrayLastIndexOfAny | Job-WGBUQW | True | 1048576 | 3.620 ns | 0.0049 ns | 0.0046 ns | - |
| JaggedLastIndexOfAny | Job-MSAXQN | False | 4294967296 | 3.717 ns | 0.0116 ns | 0.0108 ns | - |
| BigArrayLastIndexOfAny | Job-MSAXQN | False | 4294967296 | 5.105 ns | 0.0097 ns | 0.0086 ns | - |
| JaggedLastIndexOfAny | Job-WGBUQW | True | 4294967296 | 3.746 ns | 0.0059 ns | 0.0053 ns | - |
| BigArrayLastIndexOfAny | Job-WGBUQW | True | 4294967296 | 5.126 ns | 0.0131 ns | 0.0123 ns | - |
| Method | Job | Server | Length | Mean | Error | StdDev | Median | Allocated |
|---|---|---|---|---|---|---|---|---|
| JaggedBinarySearch | Job-MSAXQN | False | 1048576 | 49.96 ns | 0.011 ns | 0.010 ns | 49.96 ns | - |
| BigArrayBinarySearch | Job-MSAXQN | False | 1048576 | 18.71 ns | 0.005 ns | 0.004 ns | 18.71 ns | - |
| JaggedBinarySearch | Job-WGBUQW | True | 1048576 | 52.47 ns | 0.027 ns | 0.025 ns | 52.47 ns | - |
| BigArrayBinarySearch | Job-WGBUQW | True | 1048576 | 18.70 ns | 0.012 ns | 0.011 ns | 18.70 ns | - |
| JaggedBinarySearch | Job-MSAXQN | False | 4294967296 | 96.54 ns | 0.144 ns | 0.127 ns | 96.55 ns | - |
| BigArrayBinarySearch | Job-MSAXQN | False | 4294967296 | 85.78 ns | 0.012 ns | 0.011 ns | 85.79 ns | - |
| JaggedBinarySearch | Job-WGBUQW | True | 4294967296 | 96.07 ns | 0.578 ns | 0.541 ns | 96.05 ns | - |
| BigArrayBinarySearch | Job-WGBUQW | True | 4294967296 | 85.48 ns | 0.510 ns | 0.477 ns | 85.58 ns | - |
| JaggedCopyTo | Job-MSAXQN | False | 1048576 | 98,710.30 ns | 56.256 ns | 52.622 ns | 98,715.92 ns | - |
| BigArrayCopyTo | Job-MSAXQN | False | 1048576 | 98,145.87 ns | 20.593 ns | 17.196 ns | 98,141.40 ns | - |
| JaggedCopyTo | Job-WGBUQW | True | 1048576 | 98,778.12 ns | 29.881 ns | 26.489 ns | 98,776.77 ns | - |
| BigArrayCopyTo | Job-WGBUQW | True | 1048576 | 98,077.99 ns | 128.374 ns | 113.800 ns | 98,054.42 ns | - |
| JaggedCopyTo | Job-MSAXQN | False | 4294967296 | 873,535,712.00 ns | 1,497,004.297 ns | 1,400,298.749 ns | 873,265,018.00 ns | - |
| BigArrayCopyTo | Job-MSAXQN | False | 4294967296 | 880,486,546.20 ns | 3,345,173.644 ns | 3,129,077.503 ns | 879,058,670.00 ns | - |
| JaggedCopyTo | Job-WGBUQW | True | 4294967296 | 884,969,636.00 ns | 2,330,855.056 ns | 2,066,242.296 ns | 885,234,089.50 ns | - |
| BigArrayCopyTo | Job-WGBUQW | True | 4294967296 | 879,429,365.60 ns | 4,077,759.552 ns | 3,814,338.817 ns | 880,100,492.00 ns | - |
| JaggedFill | Job-MSAXQN | False | 1048576 | 58,367.10 ns | 180.325 ns | 168.676 ns | 58,243.54 ns | - |
| BigArrayFill | Job-MSAXQN | False | 1048576 | 45,894.35 ns | 18.216 ns | 17.039 ns | 45,899.12 ns | - |
| JaggedFill | Job-WGBUQW | True | 1048576 | 58,374.97 ns | 288.436 ns | 269.803 ns | 58,219.81 ns | - |
| BigArrayFill | Job-WGBUQW | True | 1048576 | 45,931.93 ns | 61.103 ns | 57.156 ns | 45,923.62 ns | - |
| JaggedFill | Job-MSAXQN | False | 4294967296 | 640,167,967.50 ns | 272,398.007 ns | 212,670.447 ns | 640,128,911.50 ns | - |
| BigArrayFill | Job-MSAXQN | False | 4294967296 | 639,780,247.00 ns | 1,802,039.933 ns | 1,406,914.247 ns | 640,167,750.50 ns | - |
| JaggedFill | Job-WGBUQW | True | 4294967296 | 641,006,562.00 ns | 3,491,797.738 ns | 2,915,809.256 ns | 640,610,855.00 ns | - |
| BigArrayFill | Job-WGBUQW | True | 4294967296 | 637,157,023.75 ns | 1,381,366.359 ns | 1,078,479.991 ns | 636,751,010.50 ns | - |
| JaggedSequenceEqual | Job-MSAXQN | False | 1048576 | 101,913.46 ns | 158.225 ns | 148.004 ns | 101,941.02 ns | - |
| BigArraySequenceEqual | Job-MSAXQN | False | 1048576 | 95,134.55 ns | 118.142 ns | 110.510 ns | 95,132.74 ns | - |
| JaggedSequenceEqual | Job-WGBUQW | True | 1048576 | 102,195.72 ns | 234.248 ns | 219.116 ns | 102,142.76 ns | - |
| BigArraySequenceEqual | Job-WGBUQW | True | 1048576 | 95,224.84 ns | 115.790 ns | 108.310 ns | 95,233.91 ns | - |
| JaggedSequenceEqual | Job-MSAXQN | False | 4294967296 | 1,020,484,182.52 ns | 63,587,008.014 ns | 93,205,019.553 ns | 968,937,213.00 ns | - |
| BigArraySequenceEqual | Job-MSAXQN | False | 4294967296 | 1,029,241,139.70 ns | 72,403,060.080 ns | 108,369,500.092 ns | 962,084,258.50 ns | - |
| JaggedSequenceEqual | Job-WGBUQW | True | 4294967296 | 979,557,987.45 ns | 81,645,292.812 ns | 119,674,621.446 ns | 914,186,446.00 ns | - |
| BigArraySequenceEqual | Job-WGBUQW | True | 4294967296 | 969,798,814.43 ns | 75,013,740.364 ns | 112,277,043.737 ns | 904,311,547.50 ns | - |
MIT License.